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
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
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
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>
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>
56 #include <sys/objcache.h>
57 #include <sys/event.h>
59 #include <vfs/fifofs/fifo.h>
64 * Backend for hammer2_vop_readdir()
67 hammer2_xop_readdir(hammer2_xop_t *arg, int clindex)
69 hammer2_xop_readdir_t *xop = &arg->xop_readdir;
70 hammer2_chain_t *parent;
71 hammer2_chain_t *chain;
72 hammer2_key_t key_next;
77 lkey = xop->head.lkey;
78 if (hammer2_debug & 0x0020)
79 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
82 * The inode's chain is the iterator. If we cannot acquire it our
83 * contribution ends here.
85 parent = hammer2_inode_chain(xop->head.ip, clindex,
86 HAMMER2_RESOLVE_ALWAYS |
87 HAMMER2_RESOLVE_SHARED);
89 kprintf("xop_readdir: NULL parent\n");
94 * Directory scan [re]start and loop, the feed inherits the chain's
95 * lock so do not unlock it on the iteration.
97 chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey,
98 &cache_index, HAMMER2_LOOKUP_SHARED);
100 chain = hammer2_chain_lookup(&parent, &key_next,
101 lkey, (hammer2_key_t)-1,
103 HAMMER2_LOOKUP_SHARED);
106 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
109 chain = hammer2_chain_next(&parent, chain, &key_next,
110 key_next, (hammer2_key_t)-1,
112 HAMMER2_LOOKUP_SHARED |
113 HAMMER2_LOOKUP_NOUNLOCK);
116 hammer2_chain_drop(chain);
117 hammer2_chain_unlock(parent);
118 hammer2_chain_drop(parent);
120 hammer2_xop_feed(&xop->head, NULL, clindex, error);
124 * Backend for hammer2_vop_nresolve()
127 hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex)
129 hammer2_xop_nresolve_t *xop = &arg->xop_nresolve;
130 hammer2_chain_t *parent;
131 hammer2_chain_t *chain;
132 const hammer2_inode_data_t *ripdata;
135 hammer2_key_t key_next;
137 int cache_index = -1; /* XXX */
140 parent = hammer2_inode_chain(xop->head.ip, clindex,
141 HAMMER2_RESOLVE_ALWAYS |
142 HAMMER2_RESOLVE_SHARED);
143 if (parent == NULL) {
144 kprintf("xop_nresolve: NULL parent\n");
149 name = xop->head.name;
150 name_len = xop->head.name_len;
153 * Lookup the directory entry
155 lhc = hammer2_dirhash(name, name_len);
156 chain = hammer2_chain_lookup(&parent, &key_next,
157 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
159 HAMMER2_LOOKUP_ALWAYS |
160 HAMMER2_LOOKUP_SHARED);
162 ripdata = &chain->data->ipdata;
163 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
164 ripdata->meta.name_len == name_len &&
165 bcmp(ripdata->filename, name, name_len) == 0) {
168 chain = hammer2_chain_next(&parent, chain, &key_next,
170 lhc + HAMMER2_DIRHASH_LOMASK,
172 HAMMER2_LOOKUP_ALWAYS |
173 HAMMER2_LOOKUP_SHARED);
177 * If the entry is a hardlink pointer, resolve it.
181 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
182 error = hammer2_chain_hardlink_find(
185 HAMMER2_RESOLVE_SHARED);
189 error = hammer2_xop_feed(&xop->head, chain, clindex, error);
191 hammer2_chain_drop(chain);
193 hammer2_chain_unlock(parent);
194 hammer2_chain_drop(parent);
199 * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper
200 * for hammer2_vop_nrename().
202 * This function does locates and removes the directory entry. If the
203 * entry is a hardlink pointer, this function will also remove the
204 * hardlink target if the target's nlinks is 1.
206 * The frontend is responsible for moving open inodes to the hidden directory
207 * and for decrementing nlinks.
210 hammer2_xop_unlink(hammer2_xop_t *arg, int clindex)
212 hammer2_xop_unlink_t *xop = &arg->xop_unlink;
213 hammer2_chain_t *parent;
214 hammer2_chain_t *chain;
215 const hammer2_inode_data_t *ripdata;
219 hammer2_key_t key_next;
221 int cache_index = -1; /* XXX */
225 * Requires exclusive lock
227 parent = hammer2_inode_chain(xop->head.ip, clindex,
228 HAMMER2_RESOLVE_ALWAYS);
229 if (parent == NULL) {
230 kprintf("xop_nresolve: NULL parent\n");
235 name = xop->head.name;
236 name_len = xop->head.name_len;
239 * Lookup the directory entry
241 lhc = hammer2_dirhash(name, name_len);
242 chain = hammer2_chain_lookup(&parent, &key_next,
243 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
245 HAMMER2_LOOKUP_ALWAYS);
247 ripdata = &chain->data->ipdata;
248 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
249 ripdata->meta.name_len == name_len &&
250 bcmp(ripdata->filename, name, name_len) == 0) {
253 chain = hammer2_chain_next(&parent, chain, &key_next,
255 lhc + HAMMER2_DIRHASH_LOMASK,
257 HAMMER2_LOOKUP_ALWAYS);
261 * If the directory entry is a HARDLINK pointer then obtain the
262 * underlying file type for the directory typing tests and delete
263 * the HARDLINK pointer chain permanently. The frontend is left
264 * responsible for handling nlinks on and deleting the actual inode.
266 * If the directory entry is the actual inode then use its type
267 * for the directory typing tests and delete the chain, permanency
268 * depends on whether the inode is open or not.
270 * Check directory typing and delete the entry. Note that
271 * nlinks adjustments are made on the real inode by the frontend,
276 int dopermanent = xop->dopermanent;
278 type = chain->data->ipdata.meta.type;
279 if (type == HAMMER2_OBJTYPE_HARDLINK) {
280 type = chain->data->ipdata.meta.target_type;
281 dopermanent |= HAMMER2_DELETE_PERMANENT;
283 if (type == HAMMER2_OBJTYPE_DIRECTORY &&
287 if (type != HAMMER2_OBJTYPE_DIRECTORY &&
291 hammer2_chain_delete(parent, chain, xop->dopermanent);
296 * If the entry is a hardlink pointer, resolve it. If this is the
297 * last link, delete it. We aren't the frontend so we can't adjust
301 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
302 error = hammer2_chain_hardlink_find(
307 (int64_t)chain->data->ipdata.meta.nlinks <= 1) {
308 hammer2_chain_delete(parent, chain,
315 * Chains passed to feed are expected to be locked shared.
318 hammer2_chain_unlock(chain);
319 hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS |
320 HAMMER2_RESOLVE_SHARED);
324 * We always return the hardlink target (the real inode) for
328 hammer2_xop_feed(&xop->head, chain, clindex, error);
330 hammer2_chain_drop(chain);
332 hammer2_chain_unlock(parent);
333 hammer2_chain_drop(parent);
338 * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
340 * Convert the target {dip,ip} to a hardlink target and replace
341 * the original namespace with a hardlink pointer.
344 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex)
346 hammer2_xop_nlink_t *xop = &arg->xop_nlink;
348 hammer2_inode_data_t *wipdata;
349 hammer2_chain_t *parent;
350 hammer2_chain_t *chain;
351 hammer2_chain_t *tmp;
353 hammer2_key_t key_dummy;
354 int cache_index = -1;
358 * We need the precise parent chain to issue the deletion.
362 parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
364 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
365 if (parent == NULL) {
370 chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
375 hammer2_chain_delete(parent, chain, 0);
378 * Replace the namespace with a hardlink pointer if the chain being
379 * moved is not already a hardlink target.
381 if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
383 error = hammer2_chain_create(&parent, &tmp, pmp,
385 HAMMER2_BREF_TYPE_INODE,
390 hammer2_chain_modify(tmp, 0);
391 wipdata = &tmp->data->ipdata;
392 bzero(wipdata, sizeof(*wipdata));
393 wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
394 wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
395 bcopy(chain->data->ipdata.filename, wipdata->filename,
396 chain->data->ipdata.meta.name_len);
397 wipdata->meta.target_type = chain->data->ipdata.meta.type;
398 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
399 wipdata->meta.inum = ip->meta.inum;
400 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
401 wipdata->meta.nlinks = 1;
402 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
404 hammer2_chain_unlock(tmp);
405 hammer2_chain_drop(tmp);
408 hammer2_chain_unlock(parent);
409 hammer2_chain_drop(parent);
412 * Ok, back to the deleted chain. We must reconnect this chain
413 * as a hardlink target to cdir (ip3).
415 * WARNING! Frontend assumes filename length is 18 bytes.
417 hammer2_chain_modify(chain, 0);
418 wipdata = &chain->data->ipdata;
419 ksnprintf(wipdata->filename, sizeof(wipdata->filename),
420 "0x%016jx", (intmax_t)ip->meta.inum);
421 wipdata->meta.name_len = strlen(wipdata->filename);
422 wipdata->meta.name_key = ip->meta.inum;
425 * We must seek parent properly for the create.
427 parent = hammer2_inode_chain(xop->head.ip3, clindex,
428 HAMMER2_RESOLVE_ALWAYS);
429 if (parent == NULL) {
433 tmp = hammer2_chain_lookup(&parent, &key_dummy,
434 ip->meta.inum, ip->meta.inum,
437 hammer2_chain_unlock(tmp);
438 hammer2_chain_drop(tmp);
442 error = hammer2_chain_create(&parent, &chain, pmp,
443 wipdata->meta.name_key, 0,
444 HAMMER2_BREF_TYPE_INODE,
448 * To avoid having to scan the collision space we can simply
449 * reuse the inode's original name_key. But ip->meta.name_key
450 * may have already been updated by the front-end, so use xop->lhc.
452 * (frontend is responsible for fixing up ip->pip).
455 hammer2_xop_feed(&xop->head, NULL, clindex, error);
457 hammer2_chain_unlock(parent);
458 hammer2_chain_drop(parent);
461 hammer2_chain_unlock(chain);
462 hammer2_chain_drop(chain);
467 * Backend for hammer2_vop_nrename()
469 * This handles the final step of renaming, either renaming the
470 * actual inode or renaming the hardlink pointer.
473 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
475 hammer2_xop_nrename_t *xop = &arg->xop_nrename;
477 hammer2_chain_t *parent;
478 hammer2_chain_t *chain;
479 hammer2_chain_t *tmp;
481 hammer2_key_t key_dummy;
482 int cache_index = -1;
486 * We need the precise parent chain to issue the deletion.
488 * If this is not a hardlink target we can act on the inode,
489 * otherwise we have to locate the hardlink pointer.
495 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
497 * Find ip's direct parent chain.
499 parent = hammer2_inode_chain(ip, clindex,
500 HAMMER2_RESOLVE_ALWAYS);
502 hammer2_chain_getparent(&parent,
503 HAMMER2_RESOLVE_ALWAYS);
504 if (parent == NULL) {
508 chain = hammer2_inode_chain(ip, clindex,
509 HAMMER2_RESOLVE_ALWAYS);
516 * head.ip is fdip, do a namespace search.
518 const hammer2_inode_data_t *ripdata;
520 hammer2_key_t key_next;
524 parent = hammer2_inode_chain(xop->head.ip, clindex,
525 HAMMER2_RESOLVE_ALWAYS |
526 HAMMER2_RESOLVE_SHARED);
527 if (parent == NULL) {
528 kprintf("xop_nrename: NULL parent\n");
532 name = xop->head.name;
533 name_len = xop->head.name_len;
536 * Lookup the directory entry
538 lhc = hammer2_dirhash(name, name_len);
539 chain = hammer2_chain_lookup(&parent, &key_next,
540 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
542 HAMMER2_LOOKUP_ALWAYS);
544 ripdata = &chain->data->ipdata;
545 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
546 ripdata->meta.name_len == name_len &&
547 bcmp(ripdata->filename, name, name_len) == 0) {
550 chain = hammer2_chain_next(&parent, chain, &key_next,
552 lhc + HAMMER2_DIRHASH_LOMASK,
554 HAMMER2_LOOKUP_ALWAYS);
559 * Delete it, then create it in the new namespace.
561 hammer2_chain_delete(parent, chain, 0);
562 hammer2_chain_unlock(parent);
563 hammer2_chain_drop(parent);
564 parent = NULL; /* safety */
568 * Ok, back to the deleted chain. We must reconnect this chain
569 * to tdir (ip3). The chain (a real inode or a hardlink pointer)
570 * is not otherwise modified.
572 * Frontend is expected to replicate the same inode meta data
575 * NOTE! This chain may not represent the actual inode, it
576 * can be a hardlink pointer.
578 * XXX in-inode parent directory specification?
580 if (chain->data->ipdata.meta.name_key != xop->lhc ||
581 xop->head.name_len != xop->head.name2_len ||
582 bcmp(xop->head.name, xop->head.name2, xop->head.name_len) != 0) {
583 hammer2_inode_data_t *wipdata;
585 hammer2_chain_modify(chain, 0);
586 wipdata = &chain->data->ipdata;
588 bzero(wipdata->filename, sizeof(wipdata->filename));
589 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
590 wipdata->meta.name_key = xop->lhc;
591 wipdata->meta.name_len = xop->head.name2_len;
595 * We must seek parent properly for the create.
597 parent = hammer2_inode_chain(xop->head.ip3, clindex,
598 HAMMER2_RESOLVE_ALWAYS);
599 if (parent == NULL) {
603 tmp = hammer2_chain_lookup(&parent, &key_dummy,
607 hammer2_chain_unlock(tmp);
608 hammer2_chain_drop(tmp);
613 error = hammer2_chain_create(&parent, &chain, pmp,
615 HAMMER2_BREF_TYPE_INODE,
619 * (frontend is responsible for fixing up ip->pip).
622 hammer2_xop_feed(&xop->head, NULL, clindex, error);
624 hammer2_chain_unlock(parent);
625 hammer2_chain_drop(parent);
628 hammer2_chain_unlock(chain);
629 hammer2_chain_drop(chain);
634 * Directory collision resolver scan helper (backend, threaded).
636 * Used by the inode create code to locate an unused lhc.
639 hammer2_inode_xop_scanlhc(hammer2_xop_t *arg, int clindex)
641 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
642 hammer2_chain_t *parent;
643 hammer2_chain_t *chain;
644 hammer2_key_t key_next;
645 int cache_index = -1; /* XXX */
648 parent = hammer2_inode_chain(xop->head.ip, clindex,
649 HAMMER2_RESOLVE_ALWAYS |
650 HAMMER2_RESOLVE_SHARED);
651 if (parent == NULL) {
652 kprintf("xop_nresolve: NULL parent\n");
659 * Lookup all possibly conflicting directory entries, the feed
660 * inherits the chain's lock so do not unlock it on the iteration.
662 chain = hammer2_chain_lookup(&parent, &key_next,
664 xop->lhc + HAMMER2_DIRHASH_LOMASK,
666 HAMMER2_LOOKUP_ALWAYS |
667 HAMMER2_LOOKUP_SHARED);
669 error = hammer2_xop_feed(&xop->head, chain, clindex,
672 hammer2_chain_drop(chain);
673 chain = NULL; /* safety */
676 chain = hammer2_chain_next(&parent, chain, &key_next,
678 xop->lhc + HAMMER2_DIRHASH_LOMASK,
680 HAMMER2_LOOKUP_ALWAYS |
681 HAMMER2_LOOKUP_SHARED |
682 HAMMER2_LOOKUP_NOUNLOCK);
685 hammer2_xop_feed(&xop->head, NULL, clindex, error);
687 hammer2_chain_unlock(parent);
688 hammer2_chain_drop(parent);