hammer2: Add required check to hammer2_vop_nlink()
[dragonfly.git] / sys / vfs / hammer2 / hammer2_xops.c
blob4300085c0c5c1ad466c9762a7286b21e5b1c3c25
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,
499 pmp, HAMMER2_METH_DEFAULT,
500 chain->bref.key, 0,
501 HAMMER2_BREF_TYPE_INODE,
502 HAMMER2_INODE_BYTES,
503 xop->head.mtid, 0, 0);
504 if (error)
505 goto done;
506 hammer2_chain_modify(tmp, xop->head.mtid, 0, 0);
507 wipdata = &tmp->data->ipdata;
508 bzero(wipdata, sizeof(*wipdata));
509 wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
510 wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
511 bcopy(chain->data->ipdata.filename, wipdata->filename,
512 chain->data->ipdata.meta.name_len);
513 wipdata->meta.target_type = chain->data->ipdata.meta.type;
514 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
515 wipdata->meta.inum = ip->meta.inum;
516 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
517 wipdata->meta.nlinks = 1;
518 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
520 hammer2_chain_unlock(tmp);
521 hammer2_chain_drop(tmp);
522 } else if (xop->head.ip1 != xop->head.ip3) {
524 * Delete the hardlink target so it can be moved
525 * to cdir.
527 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
528 did_delete = 1;
529 } else {
531 * Deletion not necessary (just a nlinks update).
533 did_delete = 0;
536 hammer2_chain_unlock(parent);
537 hammer2_chain_drop(parent);
538 parent = NULL;
541 * Ok, back to the deleted chain. We must reconnect this chain
542 * as a hardlink target to cdir (ip3).
544 * WARNING! Frontend assumes filename length is 18 bytes.
546 if (did_delete) {
547 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
548 wipdata = &chain->data->ipdata;
549 ksnprintf(wipdata->filename, sizeof(wipdata->filename),
550 "0x%016jx", (intmax_t)ip->meta.inum);
551 wipdata->meta.name_len = strlen(wipdata->filename);
552 wipdata->meta.name_key = ip->meta.inum;
555 * We must seek parent properly for the create to reattach
556 * chain. XXX just use chain->parent or
557 * inode_chain_and_parent() ?
559 parent = hammer2_inode_chain(xop->head.ip3, clindex,
560 HAMMER2_RESOLVE_ALWAYS);
561 if (parent == NULL) {
562 error = EIO;
563 goto done;
565 tmp = hammer2_chain_lookup(&parent, &key_dummy,
566 ip->meta.inum, ip->meta.inum,
567 &cache_index, 0);
568 if (tmp) {
569 hammer2_chain_unlock(tmp);
570 hammer2_chain_drop(tmp);
571 error = EEXIST;
572 goto done;
574 error = hammer2_chain_create(&parent, &chain,
575 pmp, HAMMER2_METH_DEFAULT,
576 wipdata->meta.name_key, 0,
577 HAMMER2_BREF_TYPE_INODE,
578 HAMMER2_INODE_BYTES,
579 xop->head.mtid, 0, 0);
580 } else {
581 error = 0;
585 * Bump nlinks to synchronize with frontend.
587 if (xop->nlinks_delta) {
588 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
589 chain->data->ipdata.meta.nlinks += xop->nlinks_delta;
593 * To avoid having to scan the collision space we can simply
594 * reuse the inode's original name_key. But ip->meta.name_key
595 * may have already been updated by the front-end, so use xop->lhc.
597 done:
598 hammer2_xop_feed(&xop->head, NULL, clindex, error);
599 if (parent) {
600 hammer2_chain_unlock(parent);
601 hammer2_chain_drop(parent);
603 if (chain) {
604 hammer2_chain_unlock(chain);
605 hammer2_chain_drop(chain);
608 #endif
611 * Backend for hammer2_vop_nrename()
613 * This handles the final step of renaming, either renaming the
614 * actual inode or renaming the hardlink pointer.
616 void
617 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
619 hammer2_xop_nrename_t *xop = &arg->xop_nrename;
620 hammer2_pfs_t *pmp;
621 hammer2_chain_t *parent;
622 hammer2_chain_t *chain;
623 hammer2_chain_t *tmp;
624 hammer2_inode_t *ip;
625 hammer2_key_t key_dummy;
626 int cache_index = -1;
627 int error;
630 * We need the precise parent chain to issue the deletion.
632 * If this is not a hardlink target we can act on the inode,
633 * otherwise we have to locate the hardlink pointer.
635 ip = xop->head.ip2;
636 pmp = ip->pmp;
637 chain = NULL;
639 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
641 * Find ip's direct parent chain.
643 parent = hammer2_inode_chain(ip, clindex,
644 HAMMER2_RESOLVE_ALWAYS);
645 if (parent)
646 hammer2_chain_getparent(&parent,
647 HAMMER2_RESOLVE_ALWAYS);
648 if (parent == NULL) {
649 error = EIO;
650 goto done;
652 chain = hammer2_inode_chain(ip, clindex,
653 HAMMER2_RESOLVE_ALWAYS);
654 if (chain == NULL) {
655 error = EIO;
656 goto done;
658 } else {
660 * The hardlink pointer for the head.ip1 hardlink target
661 * is in fdip, do a namespace search.
663 const hammer2_inode_data_t *ripdata;
664 hammer2_key_t lhc;
665 hammer2_key_t key_next;
666 const char *name;
667 size_t name_len;
669 parent = hammer2_inode_chain(xop->head.ip1, clindex,
670 HAMMER2_RESOLVE_ALWAYS);
671 if (parent == NULL) {
672 kprintf("xop_nrename: NULL parent\n");
673 error = EIO;
674 goto done;
676 name = xop->head.name1;
677 name_len = xop->head.name1_len;
680 * Lookup the directory entry
682 lhc = hammer2_dirhash(name, name_len);
683 chain = hammer2_chain_lookup(&parent, &key_next,
684 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
685 &cache_index,
686 HAMMER2_LOOKUP_ALWAYS);
687 while (chain) {
688 ripdata = &chain->data->ipdata;
689 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
690 ripdata->meta.name_len == name_len &&
691 bcmp(ripdata->filename, name, name_len) == 0) {
692 break;
694 chain = hammer2_chain_next(&parent, chain, &key_next,
695 key_next,
696 lhc + HAMMER2_DIRHASH_LOMASK,
697 &cache_index,
698 HAMMER2_LOOKUP_ALWAYS);
702 if (chain == NULL) {
703 /* XXX shouldn't happen, but does under fsstress */
704 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\" ENOENT\n",
705 xop->head.name1,
706 xop->head.name2);
707 error = ENOENT;
708 goto done;
712 * Delete it, then create it in the new namespace.
714 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
715 hammer2_chain_unlock(parent);
716 hammer2_chain_drop(parent);
717 parent = NULL; /* safety */
720 * Ok, back to the deleted chain. We must reconnect this chain
721 * to tdir (ip3). The chain (a real inode or a hardlink pointer)
722 * is not otherwise modified.
724 * Frontend is expected to replicate the same inode meta data
725 * modifications.
727 * NOTE! This chain may not represent the actual inode, it
728 * can be a hardlink pointer.
730 * XXX in-inode parent directory specification?
732 if (chain->data->ipdata.meta.name_key != xop->lhc ||
733 xop->head.name1_len != xop->head.name2_len ||
734 bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
735 hammer2_inode_data_t *wipdata;
737 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
738 wipdata = &chain->data->ipdata;
740 bzero(wipdata->filename, sizeof(wipdata->filename));
741 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
742 wipdata->meta.name_key = xop->lhc;
743 wipdata->meta.name_len = xop->head.name2_len;
745 if (chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) {
746 hammer2_inode_data_t *wipdata;
748 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
749 wipdata = &chain->data->ipdata;
751 wipdata->meta.iparent = xop->head.ip3->meta.inum;
755 * We must seek parent properly for the create.
757 parent = hammer2_inode_chain(xop->head.ip3, clindex,
758 HAMMER2_RESOLVE_ALWAYS);
759 if (parent == NULL) {
760 error = EIO;
761 goto done;
763 tmp = hammer2_chain_lookup(&parent, &key_dummy,
764 xop->lhc, xop->lhc,
765 &cache_index, 0);
766 if (tmp) {
767 hammer2_chain_unlock(tmp);
768 hammer2_chain_drop(tmp);
769 error = EEXIST;
770 goto done;
773 error = hammer2_chain_create(&parent, &chain,
774 pmp, HAMMER2_METH_DEFAULT,
775 xop->lhc, 0,
776 HAMMER2_BREF_TYPE_INODE,
777 HAMMER2_INODE_BYTES,
778 xop->head.mtid, 0, 0);
779 done:
780 hammer2_xop_feed(&xop->head, NULL, clindex, error);
781 if (parent) {
782 hammer2_chain_unlock(parent);
783 hammer2_chain_drop(parent);
785 if (chain) {
786 hammer2_chain_unlock(chain);
787 hammer2_chain_drop(chain);
792 * Directory collision resolver scan helper (backend, threaded).
794 * Used by the inode create code to locate an unused lhc.
796 void
797 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex)
799 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
800 hammer2_chain_t *parent;
801 hammer2_chain_t *chain;
802 hammer2_key_t key_next;
803 int cache_index = -1; /* XXX */
804 int error = 0;
806 parent = hammer2_inode_chain(xop->head.ip1, clindex,
807 HAMMER2_RESOLVE_ALWAYS |
808 HAMMER2_RESOLVE_SHARED);
809 if (parent == NULL) {
810 kprintf("xop_nresolve: NULL parent\n");
811 chain = NULL;
812 error = EIO;
813 goto done;
817 * Lookup all possibly conflicting directory entries, the feed
818 * inherits the chain's lock so do not unlock it on the iteration.
820 chain = hammer2_chain_lookup(&parent, &key_next,
821 xop->lhc,
822 xop->lhc + HAMMER2_DIRHASH_LOMASK,
823 &cache_index,
824 HAMMER2_LOOKUP_ALWAYS |
825 HAMMER2_LOOKUP_SHARED);
826 while (chain) {
827 error = hammer2_xop_feed(&xop->head, chain, clindex,
828 chain->error);
829 if (error) {
830 hammer2_chain_unlock(chain);
831 hammer2_chain_drop(chain);
832 chain = NULL; /* safety */
833 break;
835 chain = hammer2_chain_next(&parent, chain, &key_next,
836 key_next,
837 xop->lhc + HAMMER2_DIRHASH_LOMASK,
838 &cache_index,
839 HAMMER2_LOOKUP_ALWAYS |
840 HAMMER2_LOOKUP_SHARED);
842 done:
843 hammer2_xop_feed(&xop->head, NULL, clindex, error);
844 if (parent) {
845 hammer2_chain_unlock(parent);
846 hammer2_chain_drop(parent);
851 * Generic lookup of a specific key.
853 * Used by the inode hidden directory code to find the hidden directory.
855 void
856 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex)
858 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
859 hammer2_chain_t *parent;
860 hammer2_chain_t *chain;
861 hammer2_key_t key_next;
862 int cache_index = -1; /* XXX */
863 int error = 0;
865 parent = hammer2_inode_chain(xop->head.ip1, clindex,
866 HAMMER2_RESOLVE_ALWAYS |
867 HAMMER2_RESOLVE_SHARED);
868 chain = NULL;
869 if (parent == NULL) {
870 error = EIO;
871 goto done;
875 * Lookup all possibly conflicting directory entries, the feed
876 * inherits the chain's lock so do not unlock it on the iteration.
878 chain = hammer2_chain_lookup(&parent, &key_next,
879 xop->lhc, xop->lhc,
880 &cache_index,
881 HAMMER2_LOOKUP_ALWAYS |
882 HAMMER2_LOOKUP_SHARED);
883 if (chain)
884 hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
885 else
886 hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT);
888 done:
889 if (chain) {
890 hammer2_chain_unlock(chain);
891 hammer2_chain_drop(chain);
893 if (parent) {
894 hammer2_chain_unlock(parent);
895 hammer2_chain_drop(parent);
900 * Generic scan
902 * WARNING! Fed chains must be locked shared so ownership can be transfered
903 * and to prevent frontend/backend stalls that would occur with an
904 * exclusive lock. The shared lock also allows chain->data to be
905 * retained.
907 void
908 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex)
910 hammer2_xop_scanall_t *xop = &arg->xop_scanall;
911 hammer2_chain_t *parent;
912 hammer2_chain_t *chain;
913 hammer2_key_t key_next;
914 int cache_index = -1;
915 int error = 0;
918 * Assert required flags.
920 KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED);
921 KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED);
924 * The inode's chain is the iterator. If we cannot acquire it our
925 * contribution ends here.
927 parent = hammer2_inode_chain(xop->head.ip1, clindex,
928 xop->resolve_flags);
929 if (parent == NULL) {
930 kprintf("xop_readdir: NULL parent\n");
931 goto done;
935 * Generic scan of exact records. Note that indirect blocks are
936 * automatically recursed and will not be returned.
938 chain = hammer2_chain_lookup(&parent, &key_next,
939 xop->key_beg, xop->key_end,
940 &cache_index, xop->lookup_flags);
941 while (chain) {
942 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
943 if (error)
944 break;
945 chain = hammer2_chain_next(&parent, chain, &key_next,
946 key_next, xop->key_end,
947 &cache_index, xop->lookup_flags);
949 if (chain) {
950 hammer2_chain_unlock(chain);
951 hammer2_chain_drop(chain);
953 hammer2_chain_unlock(parent);
954 hammer2_chain_drop(parent);
955 done:
956 hammer2_xop_feed(&xop->head, NULL, clindex, error);