hammer2 - Remove the hidden directory, rework deletions
[dragonfly.git] / sys / vfs / hammer2 / hammer2_xops.c
blob192ebbfba1abc8435155a203f02bbc65f464f03a
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 static
67 int
68 checkdirempty(hammer2_chain_t *parent, hammer2_chain_t *chain, int clindex)
70 hammer2_key_t key_next;
71 int cache_index = -1;
72 int error;
74 error = 0;
75 chain = hammer2_chain_lookup_init(chain, 0);
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);
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);
95 if (chain) {
96 error = ENOTEMPTY;
97 hammer2_chain_unlock(chain);
98 hammer2_chain_drop(chain);
99 } else {
100 error = 0;
102 hammer2_chain_lookup_done(parent);
104 return error;
108 * Backend for hammer2_vfs_root()
110 * This is called when a newly mounted PFS has not yet synchronized
111 * to the inode_tid and modify_tid.
113 void
114 hammer2_xop_ipcluster(hammer2_xop_t *arg, int clindex)
116 hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster;
117 hammer2_chain_t *chain;
118 int error;
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;
128 hammer2_xop_feed(&xop->head, chain, clindex, error);
129 if (chain) {
130 hammer2_chain_unlock(chain);
131 hammer2_chain_drop(chain);
136 * Backend for hammer2_vop_readdir()
138 void
139 hammer2_xop_readdir(hammer2_xop_t *arg, int clindex)
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;
149 lkey = xop->lkey;
150 if (hammer2_debug & 0x0020)
151 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
154 * The inode's chain is the iterator. If we cannot acquire it our
155 * contribution ends here.
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;
166 * Directory scan [re]start and loop, the feed inherits the chain's
167 * lock so do not unlock it on the iteration.
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);
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);
186 if (chain) {
187 hammer2_chain_unlock(chain);
188 hammer2_chain_drop(chain);
190 hammer2_chain_unlock(parent);
191 hammer2_chain_drop(parent);
192 done:
193 hammer2_xop_feed(&xop->head, NULL, clindex, error);
197 * Backend for hammer2_vop_nresolve()
199 void
200 hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex)
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;
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;
222 name = xop->head.name1;
223 name_len = xop->head.name1_len;
226 * Lookup the directory entry
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;
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);
250 * If the entry is a hardlink pointer, resolve it.
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);
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);
266 if (parent) {
267 hammer2_chain_unlock(parent);
268 hammer2_chain_drop(parent);
273 * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper
274 * for hammer2_vop_nrename().
276 * This function locates and removes a directory entry. If the entry is
277 * a hardlink pointer, this function does NOT remove the hardlink target,
278 * but will lookup and return the hardlink target.
280 * Note that any hardlink target's nlinks may not be synchronized to the
281 * in-memory inode. hammer2_inode_unlink_finisher() is responsible for the
282 * final disposition of the hardlink target.
284 * If an inode pointer we lookup and return the actual inode. If not, we
285 * return the deleted directory entry.
287 * The frontend is responsible for moving open inodes to the hidden directory
288 * and for decrementing nlinks.
290 void
291 hammer2_xop_unlink(hammer2_xop_t *arg, int clindex)
293 hammer2_xop_unlink_t *xop = &arg->xop_unlink;
294 hammer2_chain_t *parent;
295 hammer2_chain_t *chain;
296 const hammer2_inode_data_t *ripdata;
297 const char *name;
298 size_t name_len;
299 hammer2_key_t key_next;
300 hammer2_key_t lhc;
301 int cache_index = -1; /* XXX */
302 int error;
305 * Requires exclusive lock
307 parent = hammer2_inode_chain(xop->head.ip1, clindex,
308 HAMMER2_RESOLVE_ALWAYS);
309 chain = NULL;
310 if (parent == NULL) {
311 kprintf("xop_nresolve: NULL parent\n");
312 error = EIO;
313 goto done;
315 name = xop->head.name1;
316 name_len = xop->head.name1_len;
319 * Lookup the directory entry
321 lhc = hammer2_dirhash(name, name_len);
322 chain = hammer2_chain_lookup(&parent, &key_next,
323 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
324 &cache_index,
325 HAMMER2_LOOKUP_ALWAYS);
326 while (chain) {
327 ripdata = &chain->data->ipdata;
328 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
329 ripdata->meta.name_len == name_len &&
330 bcmp(ripdata->filename, name, name_len) == 0) {
331 break;
333 chain = hammer2_chain_next(&parent, chain, &key_next,
334 key_next,
335 lhc + HAMMER2_DIRHASH_LOMASK,
336 &cache_index,
337 HAMMER2_LOOKUP_ALWAYS);
341 * The directory entry will almost always be a hardlink pointer,
342 * which we permanently delete. Otherwise we go by xop->dopermanent.
343 * Note that the target chain's nlinks may not be synchronized with
344 * the in-memory hammer2_inode_t structure, so we don't try to do
345 * anything fancy here.
347 error = 0;
348 if (chain) {
349 int dopermanent = xop->dopermanent;
350 uint8_t type;
353 * If the directory entry is the actual inode then use its
354 * type for the directory typing tests, otherwise if it is
355 * a hardlink pointer then use the secondary type field for
356 * directory typing tests.
358 * Also, hardlink pointers are always permanently deleted
359 * (because they aren't the actual inode).
361 type = chain->data->ipdata.meta.type;
362 if (type == HAMMER2_OBJTYPE_HARDLINK) {
363 type = chain->data->ipdata.meta.target_type;
364 dopermanent |= HAMMER2_DELETE_PERMANENT;
368 * Check directory typing and delete the entry. Note that
369 * nlinks adjustments are made on the real inode by the
370 * frontend, not here.
372 if (type == HAMMER2_OBJTYPE_DIRECTORY &&
373 checkdirempty(parent, chain, clindex) != 0) {
374 error = ENOTEMPTY;
375 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
376 xop->isdir == 0) {
377 error = ENOTDIR;
378 } else if (type != HAMMER2_OBJTYPE_DIRECTORY &&
379 xop->isdir >= 1) {
380 error = EISDIR;
381 } else {
383 * This deletes the directory entry itself, which is
384 * also the inode when nlinks == 1. Hardlink targets
385 * are handled in the next conditional.
387 error = chain->error;
388 hammer2_chain_delete(parent, chain,
389 xop->head.mtid, dopermanent);
394 * If the entry is a hardlink pointer, resolve it. We do not try
395 * to manipulate the contents of the hardlink target as it might
396 * not be synchronized with the front-end hammer2_inode_t. Nor do
397 * we try to lookup the front-end hammer2_inode_t here (we are the
398 * backend!).
400 if (chain &&
401 chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
402 int error2;
404 lhc = chain->data->ipdata.meta.inum;
406 error2 = hammer2_chain_hardlink_find(&parent, &chain,
407 clindex, 0);
408 if (error2) {
409 kprintf("hardlink_find: %016jx %p failed\n",
410 lhc, chain);
411 error2 = 0; /* silently ignore */
413 if (error == 0)
414 error = error2;
418 * Return the inode target for further action. Typically used by
419 * hammer2_inode_unlink_finisher().
421 done:
422 hammer2_xop_feed(&xop->head, chain, clindex, error);
423 if (chain) {
424 hammer2_chain_unlock(chain);
425 hammer2_chain_drop(chain);
426 chain = NULL;
428 if (parent) {
429 hammer2_chain_unlock(parent);
430 hammer2_chain_drop(parent);
431 parent = NULL;
435 #if 0
437 * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
439 * ip1 - fdip
440 * ip2 - ip
441 * ip3 - cdip
443 * If a hardlink pointer:
444 * The existing hardlink target {fdip,ip} must be moved to another
445 * directory {cdip,ip}
447 * If not a hardlink pointer:
448 * Convert the target {fdip,ip} to a hardlink target {cdip,ip} and
449 * replace the original namespace {fdip,name} with a hardlink pointer.
451 void
452 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex)
454 hammer2_xop_nlink_t *xop = &arg->xop_nlink;
455 hammer2_pfs_t *pmp;
456 hammer2_inode_data_t *wipdata;
457 hammer2_chain_t *parent;
458 hammer2_chain_t *chain;
459 hammer2_chain_t *tmp;
460 hammer2_inode_t *ip;
461 hammer2_key_t key_dummy;
462 int cache_index = -1;
463 int error;
464 int did_delete = 0;
467 * We need the precise parent chain to issue the deletion.
469 ip = xop->head.ip2;
470 pmp = ip->pmp;
471 parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
472 if (parent)
473 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
474 if (parent == NULL) {
475 chain = NULL;
476 error = EIO;
477 goto done;
479 chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
480 if (chain == NULL) {
481 error = EIO;
482 goto done;
484 KKASSERT(chain->parent == parent);
486 if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
488 * Delete the original chain and hold onto it for the move
489 * to cdir.
491 * Replace the namespace with a hardlink pointer if the
492 * chain being moved is not already a hardlink target.
494 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
495 did_delete = 1;
497 tmp = NULL;
498 error = hammer2_chain_create(&parent, &tmp, pmp,
499 chain->bref.key, 0,
500 HAMMER2_BREF_TYPE_INODE,
501 HAMMER2_INODE_BYTES,
502 xop->head.mtid, 0, 0);
503 if (error)
504 goto done;
505 hammer2_chain_modify(tmp, xop->head.mtid, 0, 0);
506 wipdata = &tmp->data->ipdata;
507 bzero(wipdata, sizeof(*wipdata));
508 wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
509 wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
510 bcopy(chain->data->ipdata.filename, wipdata->filename,
511 chain->data->ipdata.meta.name_len);
512 wipdata->meta.target_type = chain->data->ipdata.meta.type;
513 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
514 wipdata->meta.inum = ip->meta.inum;
515 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
516 wipdata->meta.nlinks = 1;
517 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
519 hammer2_chain_unlock(tmp);
520 hammer2_chain_drop(tmp);
521 } else if (xop->head.ip1 != xop->head.ip3) {
523 * Delete the hardlink target so it can be moved
524 * to cdir.
526 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
527 did_delete = 1;
528 } else {
530 * Deletion not necessary (just a nlinks update).
532 did_delete = 0;
535 hammer2_chain_unlock(parent);
536 hammer2_chain_drop(parent);
537 parent = NULL;
540 * Ok, back to the deleted chain. We must reconnect this chain
541 * as a hardlink target to cdir (ip3).
543 * WARNING! Frontend assumes filename length is 18 bytes.
545 if (did_delete) {
546 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
547 wipdata = &chain->data->ipdata;
548 ksnprintf(wipdata->filename, sizeof(wipdata->filename),
549 "0x%016jx", (intmax_t)ip->meta.inum);
550 wipdata->meta.name_len = strlen(wipdata->filename);
551 wipdata->meta.name_key = ip->meta.inum;
554 * We must seek parent properly for the create to reattach
555 * chain. XXX just use chain->parent or
556 * inode_chain_and_parent() ?
558 parent = hammer2_inode_chain(xop->head.ip3, clindex,
559 HAMMER2_RESOLVE_ALWAYS);
560 if (parent == NULL) {
561 error = EIO;
562 goto done;
564 tmp = hammer2_chain_lookup(&parent, &key_dummy,
565 ip->meta.inum, ip->meta.inum,
566 &cache_index, 0);
567 if (tmp) {
568 hammer2_chain_unlock(tmp);
569 hammer2_chain_drop(tmp);
570 error = EEXIST;
571 goto done;
573 error = hammer2_chain_create(&parent, &chain, pmp,
574 wipdata->meta.name_key, 0,
575 HAMMER2_BREF_TYPE_INODE,
576 HAMMER2_INODE_BYTES,
577 xop->head.mtid, 0, 0);
578 } else {
579 error = 0;
583 * Bump nlinks to synchronize with frontend.
585 if (xop->nlinks_delta) {
586 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
587 chain->data->ipdata.meta.nlinks += xop->nlinks_delta;
591 * To avoid having to scan the collision space we can simply
592 * reuse the inode's original name_key. But ip->meta.name_key
593 * may have already been updated by the front-end, so use xop->lhc.
595 done:
596 hammer2_xop_feed(&xop->head, NULL, clindex, error);
597 if (parent) {
598 hammer2_chain_unlock(parent);
599 hammer2_chain_drop(parent);
601 if (chain) {
602 hammer2_chain_unlock(chain);
603 hammer2_chain_drop(chain);
606 #endif
609 * Backend for hammer2_vop_nrename()
611 * This handles the final step of renaming, either renaming the
612 * actual inode or renaming the hardlink pointer.
614 void
615 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
617 hammer2_xop_nrename_t *xop = &arg->xop_nrename;
618 hammer2_pfs_t *pmp;
619 hammer2_chain_t *parent;
620 hammer2_chain_t *chain;
621 hammer2_chain_t *tmp;
622 hammer2_inode_t *ip;
623 hammer2_key_t key_dummy;
624 int cache_index = -1;
625 int error;
628 * We need the precise parent chain to issue the deletion.
630 * If this is not a hardlink target we can act on the inode,
631 * otherwise we have to locate the hardlink pointer.
633 ip = xop->head.ip2;
634 pmp = ip->pmp;
635 chain = NULL;
637 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
639 * Find ip's direct parent chain.
641 parent = hammer2_inode_chain(ip, clindex,
642 HAMMER2_RESOLVE_ALWAYS);
643 if (parent)
644 hammer2_chain_getparent(&parent,
645 HAMMER2_RESOLVE_ALWAYS);
646 if (parent == NULL) {
647 error = EIO;
648 goto done;
650 chain = hammer2_inode_chain(ip, clindex,
651 HAMMER2_RESOLVE_ALWAYS);
652 if (chain == NULL) {
653 error = EIO;
654 goto done;
656 } else {
658 * The hardlink pointer for the head.ip1 hardlink target
659 * is in fdip, do a namespace search.
661 const hammer2_inode_data_t *ripdata;
662 hammer2_key_t lhc;
663 hammer2_key_t key_next;
664 const char *name;
665 size_t name_len;
667 parent = hammer2_inode_chain(xop->head.ip1, clindex,
668 HAMMER2_RESOLVE_ALWAYS);
669 if (parent == NULL) {
670 kprintf("xop_nrename: NULL parent\n");
671 error = EIO;
672 goto done;
674 name = xop->head.name1;
675 name_len = xop->head.name1_len;
678 * Lookup the directory entry
680 lhc = hammer2_dirhash(name, name_len);
681 chain = hammer2_chain_lookup(&parent, &key_next,
682 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
683 &cache_index,
684 HAMMER2_LOOKUP_ALWAYS);
685 while (chain) {
686 ripdata = &chain->data->ipdata;
687 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
688 ripdata->meta.name_len == name_len &&
689 bcmp(ripdata->filename, name, name_len) == 0) {
690 break;
692 chain = hammer2_chain_next(&parent, chain, &key_next,
693 key_next,
694 lhc + HAMMER2_DIRHASH_LOMASK,
695 &cache_index,
696 HAMMER2_LOOKUP_ALWAYS);
700 if (chain == NULL) {
701 /* XXX shouldn't happen, but does under fsstress */
702 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\" ENOENT\n",
703 xop->head.name1,
704 xop->head.name2);
705 error = ENOENT;
706 goto done;
710 * Delete it, then create it in the new namespace.
712 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
713 hammer2_chain_unlock(parent);
714 hammer2_chain_drop(parent);
715 parent = NULL; /* safety */
718 * Ok, back to the deleted chain. We must reconnect this chain
719 * to tdir (ip3). The chain (a real inode or a hardlink pointer)
720 * is not otherwise modified.
722 * Frontend is expected to replicate the same inode meta data
723 * modifications.
725 * NOTE! This chain may not represent the actual inode, it
726 * can be a hardlink pointer.
728 * XXX in-inode parent directory specification?
730 if (chain->data->ipdata.meta.name_key != xop->lhc ||
731 xop->head.name1_len != xop->head.name2_len ||
732 bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
733 hammer2_inode_data_t *wipdata;
735 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
736 wipdata = &chain->data->ipdata;
738 bzero(wipdata->filename, sizeof(wipdata->filename));
739 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
740 wipdata->meta.name_key = xop->lhc;
741 wipdata->meta.name_len = xop->head.name2_len;
743 if (chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) {
744 hammer2_inode_data_t *wipdata;
746 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
747 wipdata = &chain->data->ipdata;
749 wipdata->meta.iparent = xop->head.ip3->meta.inum;
753 * We must seek parent properly for the create.
755 parent = hammer2_inode_chain(xop->head.ip3, clindex,
756 HAMMER2_RESOLVE_ALWAYS);
757 if (parent == NULL) {
758 error = EIO;
759 goto done;
761 tmp = hammer2_chain_lookup(&parent, &key_dummy,
762 xop->lhc, xop->lhc,
763 &cache_index, 0);
764 if (tmp) {
765 hammer2_chain_unlock(tmp);
766 hammer2_chain_drop(tmp);
767 error = EEXIST;
768 goto done;
771 error = hammer2_chain_create(&parent, &chain, pmp,
772 xop->lhc, 0,
773 HAMMER2_BREF_TYPE_INODE,
774 HAMMER2_INODE_BYTES,
775 xop->head.mtid, 0, 0);
776 done:
777 hammer2_xop_feed(&xop->head, NULL, clindex, error);
778 if (parent) {
779 hammer2_chain_unlock(parent);
780 hammer2_chain_drop(parent);
782 if (chain) {
783 hammer2_chain_unlock(chain);
784 hammer2_chain_drop(chain);
789 * Directory collision resolver scan helper (backend, threaded).
791 * Used by the inode create code to locate an unused lhc.
793 void
794 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex)
796 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
797 hammer2_chain_t *parent;
798 hammer2_chain_t *chain;
799 hammer2_key_t key_next;
800 int cache_index = -1; /* XXX */
801 int error = 0;
803 parent = hammer2_inode_chain(xop->head.ip1, clindex,
804 HAMMER2_RESOLVE_ALWAYS |
805 HAMMER2_RESOLVE_SHARED);
806 if (parent == NULL) {
807 kprintf("xop_nresolve: NULL parent\n");
808 chain = NULL;
809 error = EIO;
810 goto done;
814 * Lookup all possibly conflicting directory entries, the feed
815 * inherits the chain's lock so do not unlock it on the iteration.
817 chain = hammer2_chain_lookup(&parent, &key_next,
818 xop->lhc,
819 xop->lhc + HAMMER2_DIRHASH_LOMASK,
820 &cache_index,
821 HAMMER2_LOOKUP_ALWAYS |
822 HAMMER2_LOOKUP_SHARED);
823 while (chain) {
824 error = hammer2_xop_feed(&xop->head, chain, clindex,
825 chain->error);
826 if (error) {
827 hammer2_chain_unlock(chain);
828 hammer2_chain_drop(chain);
829 chain = NULL; /* safety */
830 break;
832 chain = hammer2_chain_next(&parent, chain, &key_next,
833 key_next,
834 xop->lhc + HAMMER2_DIRHASH_LOMASK,
835 &cache_index,
836 HAMMER2_LOOKUP_ALWAYS |
837 HAMMER2_LOOKUP_SHARED);
839 done:
840 hammer2_xop_feed(&xop->head, NULL, clindex, error);
841 if (parent) {
842 hammer2_chain_unlock(parent);
843 hammer2_chain_drop(parent);
848 * Generic lookup of a specific key.
850 * Used by the inode hidden directory code to find the hidden directory.
852 void
853 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex)
855 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
856 hammer2_chain_t *parent;
857 hammer2_chain_t *chain;
858 hammer2_key_t key_next;
859 int cache_index = -1; /* XXX */
860 int error = 0;
862 parent = hammer2_inode_chain(xop->head.ip1, clindex,
863 HAMMER2_RESOLVE_ALWAYS |
864 HAMMER2_RESOLVE_SHARED);
865 chain = NULL;
866 if (parent == NULL) {
867 error = EIO;
868 goto done;
872 * Lookup all possibly conflicting directory entries, the feed
873 * inherits the chain's lock so do not unlock it on the iteration.
875 chain = hammer2_chain_lookup(&parent, &key_next,
876 xop->lhc, xop->lhc,
877 &cache_index,
878 HAMMER2_LOOKUP_ALWAYS |
879 HAMMER2_LOOKUP_SHARED);
880 if (chain)
881 hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
882 else
883 hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT);
885 done:
886 if (chain) {
887 hammer2_chain_unlock(chain);
888 hammer2_chain_drop(chain);
890 if (parent) {
891 hammer2_chain_unlock(parent);
892 hammer2_chain_drop(parent);
897 * Generic scan
899 * WARNING! Fed chains must be locked shared so ownership can be transfered
900 * and to prevent frontend/backend stalls that would occur with an
901 * exclusive lock. The shared lock also allows chain->data to be
902 * retained.
904 void
905 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex)
907 hammer2_xop_scanall_t *xop = &arg->xop_scanall;
908 hammer2_chain_t *parent;
909 hammer2_chain_t *chain;
910 hammer2_key_t key_next;
911 int cache_index = -1;
912 int error = 0;
915 * Assert required flags.
917 KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED);
918 KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED);
921 * The inode's chain is the iterator. If we cannot acquire it our
922 * contribution ends here.
924 parent = hammer2_inode_chain(xop->head.ip1, clindex,
925 xop->resolve_flags);
926 if (parent == NULL) {
927 kprintf("xop_readdir: NULL parent\n");
928 goto done;
932 * Generic scan of exact records. Note that indirect blocks are
933 * automatically recursed and will not be returned.
935 chain = hammer2_chain_lookup(&parent, &key_next,
936 xop->key_beg, xop->key_end,
937 &cache_index, xop->lookup_flags);
938 while (chain) {
939 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
940 if (error)
941 break;
942 chain = hammer2_chain_next(&parent, chain, &key_next,
943 key_next, xop->key_end,
944 &cache_index, xop->lookup_flags);
946 if (chain) {
947 hammer2_chain_unlock(chain);
948 hammer2_chain_drop(chain);
950 hammer2_chain_unlock(parent);
951 hammer2_chain_drop(parent);
952 done:
953 hammer2_xop_feed(&xop->head, NULL, clindex, error);