hammer2 - Start work on inode indexing - MAJOR CHANGE
[dragonfly.git] / sys / vfs / hammer2 / hammer2_xops.c
blob6179ccdedc1c9de95437d289127f24b9491a3e13
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 *orig_parent)
70 hammer2_chain_t *parent;
71 hammer2_chain_t *chain;
72 hammer2_key_t key_next;
73 int cache_index = -1;
74 int error;
76 parent = hammer2_chain_lookup_init(orig_parent, 0);
77 chain = hammer2_chain_lookup(&parent, &key_next,
78 HAMMER2_DIRHASH_VISIBLE,
79 HAMMER2_KEY_MAX,
80 &cache_index, 0);
81 error = chain ? ENOTEMPTY : 0;
82 if (chain) {
83 hammer2_chain_unlock(chain);
84 hammer2_chain_drop(chain);
86 hammer2_chain_lookup_done(parent);
88 return error;
92 * Backend for hammer2_vfs_root()
94 * This is called when a newly mounted PFS has not yet synchronized
95 * to the inode_tid and modify_tid.
97 void
98 hammer2_xop_ipcluster(hammer2_xop_t *arg, int clindex)
100 hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster;
101 hammer2_chain_t *chain;
102 int error;
104 chain = hammer2_inode_chain(xop->head.ip1, clindex,
105 HAMMER2_RESOLVE_ALWAYS |
106 HAMMER2_RESOLVE_SHARED);
107 if (chain)
108 error = chain->error;
109 else
110 error = EIO;
112 hammer2_xop_feed(&xop->head, chain, clindex, error);
113 if (chain) {
114 hammer2_chain_unlock(chain);
115 hammer2_chain_drop(chain);
120 * Backend for hammer2_vop_readdir()
122 void
123 hammer2_xop_readdir(hammer2_xop_t *arg, int clindex)
125 hammer2_xop_readdir_t *xop = &arg->xop_readdir;
126 hammer2_chain_t *parent;
127 hammer2_chain_t *chain;
128 hammer2_key_t key_next;
129 hammer2_key_t lkey;
130 int cache_index = -1;
131 int error = 0;
133 lkey = xop->lkey;
134 if (hammer2_debug & 0x0020)
135 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
138 * The inode's chain is the iterator. If we cannot acquire it our
139 * contribution ends here.
141 parent = hammer2_inode_chain(xop->head.ip1, clindex,
142 HAMMER2_RESOLVE_ALWAYS |
143 HAMMER2_RESOLVE_SHARED);
144 if (parent == NULL) {
145 kprintf("xop_readdir: NULL parent\n");
146 goto done;
150 * Directory scan [re]start and loop, the feed inherits the chain's
151 * lock so do not unlock it on the iteration.
153 chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey,
154 &cache_index, HAMMER2_LOOKUP_SHARED);
155 if (chain == NULL) {
156 chain = hammer2_chain_lookup(&parent, &key_next,
157 lkey, HAMMER2_KEY_MAX,
158 &cache_index,
159 HAMMER2_LOOKUP_SHARED);
161 while (chain) {
162 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
163 if (error)
164 break;
165 chain = hammer2_chain_next(&parent, chain, &key_next,
166 key_next, HAMMER2_KEY_MAX,
167 &cache_index,
168 HAMMER2_LOOKUP_SHARED);
170 if (chain) {
171 hammer2_chain_unlock(chain);
172 hammer2_chain_drop(chain);
174 hammer2_chain_unlock(parent);
175 hammer2_chain_drop(parent);
176 done:
177 hammer2_xop_feed(&xop->head, NULL, clindex, error);
181 * Backend for hammer2_vop_nresolve()
183 void
184 hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex)
186 hammer2_xop_nresolve_t *xop = &arg->xop_nresolve;
187 hammer2_chain_t *parent;
188 hammer2_chain_t *chain;
189 const hammer2_inode_data_t *ripdata;
190 const char *name;
191 size_t name_len;
192 hammer2_key_t key_next;
193 hammer2_key_t lhc;
194 int cache_index = -1; /* XXX */
195 int error;
197 parent = hammer2_inode_chain(xop->head.ip1, clindex,
198 HAMMER2_RESOLVE_ALWAYS |
199 HAMMER2_RESOLVE_SHARED);
200 if (parent == NULL) {
201 kprintf("xop_nresolve: NULL parent\n");
202 chain = NULL;
203 error = EIO;
204 goto done;
206 name = xop->head.name1;
207 name_len = xop->head.name1_len;
210 * Lookup the directory entry
212 lhc = hammer2_dirhash(name, name_len);
213 chain = hammer2_chain_lookup(&parent, &key_next,
214 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
215 &cache_index,
216 HAMMER2_LOOKUP_ALWAYS |
217 HAMMER2_LOOKUP_SHARED);
218 while (chain) {
219 ripdata = &chain->data->ipdata;
220 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
221 ripdata->meta.name_len == name_len &&
222 bcmp(ripdata->filename, name, name_len) == 0) {
223 break;
225 chain = hammer2_chain_next(&parent, chain, &key_next,
226 key_next,
227 lhc + HAMMER2_DIRHASH_LOMASK,
228 &cache_index,
229 HAMMER2_LOOKUP_ALWAYS |
230 HAMMER2_LOOKUP_SHARED);
234 * If the entry is a hardlink pointer, resolve it.
236 error = 0;
237 if (chain) {
238 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
239 error = hammer2_chain_hardlink_find(
240 xop->head.ip1,
241 &parent, &chain,
242 HAMMER2_LOOKUP_SHARED);
245 done:
246 error = hammer2_xop_feed(&xop->head, chain, clindex, error);
247 if (chain) {
248 hammer2_chain_unlock(chain);
249 hammer2_chain_drop(chain);
251 if (parent) {
252 hammer2_chain_unlock(parent);
253 hammer2_chain_drop(parent);
258 * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper
259 * for hammer2_vop_nrename().
261 * This function locates and removes the directory entry. If the
262 * entry is a hardlink pointer, this function will also remove the
263 * hardlink target if the target's nlinks is 1.
265 * The frontend is responsible for moving open inodes to the hidden directory
266 * and for decrementing nlinks.
268 void
269 hammer2_xop_unlink(hammer2_xop_t *arg, int clindex)
271 hammer2_xop_unlink_t *xop = &arg->xop_unlink;
272 hammer2_chain_t *parent;
273 hammer2_chain_t *chain;
274 const hammer2_inode_data_t *ripdata;
275 const char *name;
276 size_t name_len;
277 hammer2_key_t key_next;
278 hammer2_key_t lhc;
279 int cache_index = -1; /* XXX */
280 int error;
283 * Requires exclusive lock
285 parent = hammer2_inode_chain(xop->head.ip1, clindex,
286 HAMMER2_RESOLVE_ALWAYS);
287 chain = NULL;
288 if (parent == NULL) {
289 kprintf("xop_nresolve: NULL parent\n");
290 error = EIO;
291 goto done;
293 name = xop->head.name1;
294 name_len = xop->head.name1_len;
297 * Lookup the directory entry
299 lhc = hammer2_dirhash(name, name_len);
300 chain = hammer2_chain_lookup(&parent, &key_next,
301 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
302 &cache_index,
303 HAMMER2_LOOKUP_ALWAYS);
304 while (chain) {
305 ripdata = &chain->data->ipdata;
306 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
307 ripdata->meta.name_len == name_len &&
308 bcmp(ripdata->filename, name, name_len) == 0) {
309 break;
311 chain = hammer2_chain_next(&parent, chain, &key_next,
312 key_next,
313 lhc + HAMMER2_DIRHASH_LOMASK,
314 &cache_index,
315 HAMMER2_LOOKUP_ALWAYS);
319 * If the directory entry is a HARDLINK pointer then obtain the
320 * underlying file type for the directory typing tests and delete
321 * the HARDLINK pointer chain permanently. The frontend is left
322 * responsible for handling nlinks on and deleting the actual inode.
324 * If the directory entry is the actual inode then use its type
325 * for the directory typing tests and delete the chain, permanency
326 * depends on whether the inode is open or not.
328 * Check directory typing and delete the entry. Note that
329 * nlinks adjustments are made on the real inode by the frontend,
330 * not here.
332 error = 0;
333 if (chain) {
334 int dopermanent = xop->dopermanent;
335 uint8_t type;
337 type = chain->data->ipdata.meta.type;
338 if (type == HAMMER2_OBJTYPE_HARDLINK) {
339 type = chain->data->ipdata.meta.target_type;
340 dopermanent |= HAMMER2_DELETE_PERMANENT;
343 if (type == HAMMER2_OBJTYPE_DIRECTORY &&
344 checkdirempty(chain) != 0) {
345 error = ENOTEMPTY;
346 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
347 xop->isdir == 0) {
348 error = ENOTDIR;
349 } else
350 if (type != HAMMER2_OBJTYPE_DIRECTORY &&
351 xop->isdir >= 1) {
352 error = EISDIR;
353 } else {
355 * This deletes the directory entry itself, which is
356 * also the inode when nlinks == 1. Hardlink targets
357 * are handled in the next conditional.
359 error = chain->error;
360 hammer2_chain_delete(parent, chain,
361 xop->head.mtid, dopermanent);
366 * If the entry is a hardlink pointer, resolve it. If this is the
367 * last link, delete it. The frontend has the master copy of nlinks
368 * but we still have to make adjustments here to synchronize with it.
370 * On delete / adjust nlinks if there is no error. But we still need
371 * to resolve the hardlink to feed the inode's real chain back to
372 * the frontend.
374 * XXX we are basically tracking the nlinks count by doing a delta
375 * adjustment instead of having the frontend pass the absolute
376 * value down. We really need to have the frontend pass the
377 * absolute value down (difficult because there might not be
378 * an 'ip'). See also hammer2_xop_nlink().
380 if (chain &&
381 chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
382 int error2;
384 error2 = hammer2_chain_hardlink_find(xop->head.ip1,
385 &parent, &chain, 0);
386 if (chain && error == 0 && error2 == 0 &&
387 (int64_t)chain->data->ipdata.meta.nlinks <= 1) {
388 hammer2_chain_delete(parent, chain,
389 xop->head.mtid,
390 xop->dopermanent);
391 } else if (chain && error == 0 && error2 == 0) {
392 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
393 --chain->data->ipdata.meta.nlinks;
395 if (error == 0)
396 error = error2;
400 * Chains passed to feed are expected to be locked shared.
402 if (chain)
403 hammer2_chain_lock_downgrade(chain);
406 * We always return the hardlink target (the real inode) for
407 * further action.
409 done:
410 hammer2_xop_feed(&xop->head, chain, clindex, error);
411 if (chain) {
412 hammer2_chain_unlock(chain);
413 hammer2_chain_drop(chain);
414 chain = NULL;
416 if (parent) {
417 hammer2_chain_unlock(parent);
418 hammer2_chain_drop(parent);
419 parent = NULL;
423 #if 0
425 * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
427 * ip1 - fdip
428 * ip2 - ip
429 * ip3 - cdip
431 * If a hardlink pointer:
432 * The existing hardlink target {fdip,ip} must be moved to another
433 * directory {cdip,ip}
435 * If not a hardlink pointer:
436 * Convert the target {fdip,ip} to a hardlink target {cdip,ip} and
437 * replace the original namespace {fdip,name} with a hardlink pointer.
439 void
440 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex)
442 hammer2_xop_nlink_t *xop = &arg->xop_nlink;
443 hammer2_pfs_t *pmp;
444 hammer2_inode_data_t *wipdata;
445 hammer2_chain_t *parent;
446 hammer2_chain_t *chain;
447 hammer2_chain_t *tmp;
448 hammer2_inode_t *ip;
449 hammer2_key_t key_dummy;
450 int cache_index = -1;
451 int error;
452 int did_delete = 0;
455 * We need the precise parent chain to issue the deletion.
457 ip = xop->head.ip2;
458 pmp = ip->pmp;
459 parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
460 if (parent)
461 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
462 if (parent == NULL) {
463 chain = NULL;
464 error = EIO;
465 goto done;
467 chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
468 if (chain == NULL) {
469 error = EIO;
470 goto done;
472 KKASSERT(chain->parent == parent);
474 if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
476 * Delete the original chain and hold onto it for the move
477 * to cdir.
479 * Replace the namespace with a hardlink pointer if the
480 * chain being moved is not already a hardlink target.
482 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
483 did_delete = 1;
485 tmp = NULL;
486 error = hammer2_chain_create(&parent, &tmp, pmp,
487 chain->bref.key, 0,
488 HAMMER2_BREF_TYPE_INODE,
489 HAMMER2_INODE_BYTES,
490 xop->head.mtid, 0, 0);
491 if (error)
492 goto done;
493 hammer2_chain_modify(tmp, xop->head.mtid, 0, 0);
494 wipdata = &tmp->data->ipdata;
495 bzero(wipdata, sizeof(*wipdata));
496 wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
497 wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
498 bcopy(chain->data->ipdata.filename, wipdata->filename,
499 chain->data->ipdata.meta.name_len);
500 wipdata->meta.target_type = chain->data->ipdata.meta.type;
501 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
502 wipdata->meta.inum = ip->meta.inum;
503 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
504 wipdata->meta.nlinks = 1;
505 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
507 hammer2_chain_unlock(tmp);
508 hammer2_chain_drop(tmp);
509 } else if (xop->head.ip1 != xop->head.ip3) {
511 * Delete the hardlink target so it can be moved
512 * to cdir.
514 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
515 did_delete = 1;
516 } else {
518 * Deletion not necessary (just a nlinks update).
520 did_delete = 0;
523 hammer2_chain_unlock(parent);
524 hammer2_chain_drop(parent);
525 parent = NULL;
528 * Ok, back to the deleted chain. We must reconnect this chain
529 * as a hardlink target to cdir (ip3).
531 * WARNING! Frontend assumes filename length is 18 bytes.
533 if (did_delete) {
534 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
535 wipdata = &chain->data->ipdata;
536 ksnprintf(wipdata->filename, sizeof(wipdata->filename),
537 "0x%016jx", (intmax_t)ip->meta.inum);
538 wipdata->meta.name_len = strlen(wipdata->filename);
539 wipdata->meta.name_key = ip->meta.inum;
542 * We must seek parent properly for the create to reattach
543 * chain. XXX just use chain->parent or
544 * inode_chain_and_parent() ?
546 parent = hammer2_inode_chain(xop->head.ip3, clindex,
547 HAMMER2_RESOLVE_ALWAYS);
548 if (parent == NULL) {
549 error = EIO;
550 goto done;
552 tmp = hammer2_chain_lookup(&parent, &key_dummy,
553 ip->meta.inum, ip->meta.inum,
554 &cache_index, 0);
555 if (tmp) {
556 hammer2_chain_unlock(tmp);
557 hammer2_chain_drop(tmp);
558 error = EEXIST;
559 goto done;
561 error = hammer2_chain_create(&parent, &chain, pmp,
562 wipdata->meta.name_key, 0,
563 HAMMER2_BREF_TYPE_INODE,
564 HAMMER2_INODE_BYTES,
565 xop->head.mtid, 0, 0);
566 } else {
567 error = 0;
571 * Bump nlinks to synchronize with frontend.
573 if (xop->nlinks_delta) {
574 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
575 chain->data->ipdata.meta.nlinks += xop->nlinks_delta;
579 * To avoid having to scan the collision space we can simply
580 * reuse the inode's original name_key. But ip->meta.name_key
581 * may have already been updated by the front-end, so use xop->lhc.
583 * (frontend is responsible for fixing up ip->pip).
585 done:
586 hammer2_xop_feed(&xop->head, NULL, clindex, error);
587 if (parent) {
588 hammer2_chain_unlock(parent);
589 hammer2_chain_drop(parent);
591 if (chain) {
592 hammer2_chain_unlock(chain);
593 hammer2_chain_drop(chain);
596 #endif
599 * Backend for hammer2_vop_nrename()
601 * This handles the final step of renaming, either renaming the
602 * actual inode or renaming the hardlink pointer.
604 void
605 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
607 hammer2_xop_nrename_t *xop = &arg->xop_nrename;
608 hammer2_pfs_t *pmp;
609 hammer2_chain_t *parent;
610 hammer2_chain_t *chain;
611 hammer2_chain_t *tmp;
612 hammer2_inode_t *ip;
613 hammer2_key_t key_dummy;
614 int cache_index = -1;
615 int error;
618 * We need the precise parent chain to issue the deletion.
620 * If this is not a hardlink target we can act on the inode,
621 * otherwise we have to locate the hardlink pointer.
623 ip = xop->head.ip2;
624 pmp = ip->pmp;
625 chain = NULL;
627 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
629 * Find ip's direct parent chain.
631 parent = hammer2_inode_chain(ip, clindex,
632 HAMMER2_RESOLVE_ALWAYS);
633 if (parent)
634 hammer2_chain_getparent(&parent,
635 HAMMER2_RESOLVE_ALWAYS);
636 if (parent == NULL) {
637 error = EIO;
638 goto done;
640 chain = hammer2_inode_chain(ip, clindex,
641 HAMMER2_RESOLVE_ALWAYS);
642 if (chain == NULL) {
643 error = EIO;
644 goto done;
646 } else {
648 * The hardlink pointer for the head.ip1 hardlink target
649 * is in fdip, do a namespace search.
651 const hammer2_inode_data_t *ripdata;
652 hammer2_key_t lhc;
653 hammer2_key_t key_next;
654 const char *name;
655 size_t name_len;
657 parent = hammer2_inode_chain(xop->head.ip1, clindex,
658 HAMMER2_RESOLVE_ALWAYS);
659 if (parent == NULL) {
660 kprintf("xop_nrename: NULL parent\n");
661 error = EIO;
662 goto done;
664 name = xop->head.name1;
665 name_len = xop->head.name1_len;
668 * Lookup the directory entry
670 lhc = hammer2_dirhash(name, name_len);
671 chain = hammer2_chain_lookup(&parent, &key_next,
672 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
673 &cache_index,
674 HAMMER2_LOOKUP_ALWAYS);
675 while (chain) {
676 ripdata = &chain->data->ipdata;
677 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
678 ripdata->meta.name_len == name_len &&
679 bcmp(ripdata->filename, name, name_len) == 0) {
680 break;
682 chain = hammer2_chain_next(&parent, chain, &key_next,
683 key_next,
684 lhc + HAMMER2_DIRHASH_LOMASK,
685 &cache_index,
686 HAMMER2_LOOKUP_ALWAYS);
690 if (chain == NULL) {
691 /* XXX shouldn't happen, but does under fsstress */
692 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\" ENOENT\n",
693 xop->head.name1,
694 xop->head.name2);
695 error = ENOENT;
696 goto done;
700 * Delete it, then create it in the new namespace.
702 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
703 hammer2_chain_unlock(parent);
704 hammer2_chain_drop(parent);
705 parent = NULL; /* safety */
708 * Ok, back to the deleted chain. We must reconnect this chain
709 * to tdir (ip3). The chain (a real inode or a hardlink pointer)
710 * is not otherwise modified.
712 * Frontend is expected to replicate the same inode meta data
713 * modifications.
715 * NOTE! This chain may not represent the actual inode, it
716 * can be a hardlink pointer.
718 * XXX in-inode parent directory specification?
720 if (chain->data->ipdata.meta.name_key != xop->lhc ||
721 xop->head.name1_len != xop->head.name2_len ||
722 bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
723 hammer2_inode_data_t *wipdata;
725 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
726 wipdata = &chain->data->ipdata;
728 bzero(wipdata->filename, sizeof(wipdata->filename));
729 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
730 wipdata->meta.name_key = xop->lhc;
731 wipdata->meta.name_len = xop->head.name2_len;
735 * We must seek parent properly for the create.
737 parent = hammer2_inode_chain(xop->head.ip3, clindex,
738 HAMMER2_RESOLVE_ALWAYS);
739 if (parent == NULL) {
740 error = EIO;
741 goto done;
743 tmp = hammer2_chain_lookup(&parent, &key_dummy,
744 xop->lhc, xop->lhc,
745 &cache_index, 0);
746 if (tmp) {
747 hammer2_chain_unlock(tmp);
748 hammer2_chain_drop(tmp);
749 error = EEXIST;
750 goto done;
753 error = hammer2_chain_create(&parent, &chain, pmp,
754 xop->lhc, 0,
755 HAMMER2_BREF_TYPE_INODE,
756 HAMMER2_INODE_BYTES,
757 xop->head.mtid, 0, 0);
759 * (frontend is responsible for fixing up ip->pip).
761 done:
762 hammer2_xop_feed(&xop->head, NULL, clindex, error);
763 if (parent) {
764 hammer2_chain_unlock(parent);
765 hammer2_chain_drop(parent);
767 if (chain) {
768 hammer2_chain_unlock(chain);
769 hammer2_chain_drop(chain);
774 * Directory collision resolver scan helper (backend, threaded).
776 * Used by the inode create code to locate an unused lhc.
778 void
779 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex)
781 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
782 hammer2_chain_t *parent;
783 hammer2_chain_t *chain;
784 hammer2_key_t key_next;
785 int cache_index = -1; /* XXX */
786 int error = 0;
788 parent = hammer2_inode_chain(xop->head.ip1, clindex,
789 HAMMER2_RESOLVE_ALWAYS |
790 HAMMER2_RESOLVE_SHARED);
791 if (parent == NULL) {
792 kprintf("xop_nresolve: NULL parent\n");
793 chain = NULL;
794 error = EIO;
795 goto done;
799 * Lookup all possibly conflicting directory entries, the feed
800 * inherits the chain's lock so do not unlock it on the iteration.
802 chain = hammer2_chain_lookup(&parent, &key_next,
803 xop->lhc,
804 xop->lhc + HAMMER2_DIRHASH_LOMASK,
805 &cache_index,
806 HAMMER2_LOOKUP_ALWAYS |
807 HAMMER2_LOOKUP_SHARED);
808 while (chain) {
809 error = hammer2_xop_feed(&xop->head, chain, clindex,
810 chain->error);
811 if (error) {
812 hammer2_chain_unlock(chain);
813 hammer2_chain_drop(chain);
814 chain = NULL; /* safety */
815 break;
817 chain = hammer2_chain_next(&parent, chain, &key_next,
818 key_next,
819 xop->lhc + HAMMER2_DIRHASH_LOMASK,
820 &cache_index,
821 HAMMER2_LOOKUP_ALWAYS |
822 HAMMER2_LOOKUP_SHARED);
824 done:
825 hammer2_xop_feed(&xop->head, NULL, clindex, error);
826 if (parent) {
827 hammer2_chain_unlock(parent);
828 hammer2_chain_drop(parent);
833 * Generic lookup of a specific key.
835 * Used by the inode hidden directory code to find the hidden directory.
837 void
838 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex)
840 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
841 hammer2_chain_t *parent;
842 hammer2_chain_t *chain;
843 hammer2_key_t key_next;
844 int cache_index = -1; /* XXX */
845 int error = 0;
847 parent = hammer2_inode_chain(xop->head.ip1, clindex,
848 HAMMER2_RESOLVE_ALWAYS |
849 HAMMER2_RESOLVE_SHARED);
850 chain = NULL;
851 if (parent == NULL) {
852 error = EIO;
853 goto done;
857 * Lookup all possibly conflicting directory entries, the feed
858 * inherits the chain's lock so do not unlock it on the iteration.
860 chain = hammer2_chain_lookup(&parent, &key_next,
861 xop->lhc, xop->lhc,
862 &cache_index,
863 HAMMER2_LOOKUP_ALWAYS |
864 HAMMER2_LOOKUP_SHARED);
865 if (chain)
866 hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
867 else
868 hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT);
870 done:
871 if (chain) {
872 hammer2_chain_unlock(chain);
873 hammer2_chain_drop(chain);
875 if (parent) {
876 hammer2_chain_unlock(parent);
877 hammer2_chain_drop(parent);
882 * Generic scan
884 * WARNING! Fed chains must be locked shared so ownership can be transfered
885 * and to prevent frontend/backend stalls that would occur with an
886 * exclusive lock. The shared lock also allows chain->data to be
887 * retained.
889 void
890 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex)
892 hammer2_xop_scanall_t *xop = &arg->xop_scanall;
893 hammer2_chain_t *parent;
894 hammer2_chain_t *chain;
895 hammer2_key_t key_next;
896 int cache_index = -1;
897 int error = 0;
900 * Assert required flags.
902 KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED);
903 KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED);
906 * The inode's chain is the iterator. If we cannot acquire it our
907 * contribution ends here.
909 parent = hammer2_inode_chain(xop->head.ip1, clindex,
910 xop->resolve_flags);
911 if (parent == NULL) {
912 kprintf("xop_readdir: NULL parent\n");
913 goto done;
917 * Generic scan of exact records. Note that indirect blocks are
918 * automatically recursed and will not be returned.
920 chain = hammer2_chain_lookup(&parent, &key_next,
921 xop->key_beg, xop->key_end,
922 &cache_index, xop->lookup_flags);
923 while (chain) {
924 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
925 if (error)
926 break;
927 chain = hammer2_chain_next(&parent, chain, &key_next,
928 key_next, xop->key_end,
929 &cache_index, xop->lookup_flags);
931 if (chain) {
932 hammer2_chain_unlock(chain);
933 hammer2_chain_drop(chain);
935 hammer2_chain_unlock(parent);
936 hammer2_chain_drop(parent);
937 done:
938 hammer2_xop_feed(&xop->head, NULL, clindex, error);