xref: /dragonfly/sys/vfs/hammer2/hammer2_xops.c (revision 5a08817b)
1 /*
2  * Copyright (c) 2011-2015 The DragonFly Project.  All rights reserved.
3  *
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)
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
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.
22  *
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.
35  */
36 /*
37  * Per-node backend for kernel filesystem interface.
38  *
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.
43  */
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>
60 
61 #include "hammer2.h"
62 
63 /*
64  * Backend for hammer2_vfs_root()
65  *
66  * This is called when a newly mounted PFS has not yet synchronized
67  * to the inode_tid and modify_tid.
68  */
69 void
70 hammer2_xop_ipcluster(hammer2_xop_t *arg, int clindex)
71 {
72 	hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster;
73 	hammer2_chain_t *chain;
74 	int error;
75 
76 	chain = hammer2_inode_chain(xop->head.ip1, clindex,
77 				    HAMMER2_RESOLVE_ALWAYS |
78 				    HAMMER2_RESOLVE_SHARED);
79 	if (chain)
80 		error = chain->error;
81 	else
82 		error = EIO;
83 
84 	hammer2_xop_feed(&xop->head, chain, clindex, error);
85 	if (chain)
86 		hammer2_chain_drop(chain);
87 }
88 
89 /*
90  * Backend for hammer2_vop_readdir()
91  */
92 void
93 hammer2_xop_readdir(hammer2_xop_t *arg, int clindex)
94 {
95 	hammer2_xop_readdir_t *xop = &arg->xop_readdir;
96 	hammer2_chain_t *parent;
97 	hammer2_chain_t *chain;
98 	hammer2_key_t key_next;
99 	hammer2_key_t lkey;
100 	int cache_index = -1;
101 	int error = 0;
102 
103 	lkey = xop->lkey;
104 	if (hammer2_debug & 0x0020)
105 		kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
106 
107 	/*
108 	 * The inode's chain is the iterator.  If we cannot acquire it our
109 	 * contribution ends here.
110 	 */
111 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
112 				     HAMMER2_RESOLVE_ALWAYS |
113 				     HAMMER2_RESOLVE_SHARED);
114 	if (parent == NULL) {
115 		kprintf("xop_readdir: NULL parent\n");
116 		goto done;
117 	}
118 
119 	/*
120 	 * Directory scan [re]start and loop, the feed inherits the chain's
121 	 * lock so do not unlock it on the iteration.
122 	 */
123 	chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey,
124 				     &cache_index, HAMMER2_LOOKUP_SHARED);
125 	if (chain == NULL) {
126 		chain = hammer2_chain_lookup(&parent, &key_next,
127 					     lkey, HAMMER2_KEY_MAX,
128 					     &cache_index,
129 					     HAMMER2_LOOKUP_SHARED);
130 	}
131 	while (chain) {
132 		error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
133 		if (error)
134 			break;
135 		chain = hammer2_chain_next(&parent, chain, &key_next,
136 					   key_next, HAMMER2_KEY_MAX,
137 					   &cache_index,
138 					   HAMMER2_LOOKUP_SHARED |
139 					   HAMMER2_LOOKUP_NOUNLOCK);
140 	}
141 	if (chain)
142 		hammer2_chain_drop(chain);
143 	hammer2_chain_unlock(parent);
144 	hammer2_chain_drop(parent);
145 done:
146 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
147 }
148 
149 /*
150  * Backend for hammer2_vop_nresolve()
151  */
152 void
153 hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex)
154 {
155 	hammer2_xop_nresolve_t *xop = &arg->xop_nresolve;
156 	hammer2_chain_t *parent;
157 	hammer2_chain_t *chain;
158 	const hammer2_inode_data_t *ripdata;
159 	const char *name;
160 	size_t name_len;
161 	hammer2_key_t key_next;
162 	hammer2_key_t lhc;
163 	int cache_index = -1;	/* XXX */
164 	int error;
165 
166 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
167 				     HAMMER2_RESOLVE_ALWAYS |
168 				     HAMMER2_RESOLVE_SHARED);
169 	if (parent == NULL) {
170 		kprintf("xop_nresolve: NULL parent\n");
171 		chain = NULL;
172 		error = EIO;
173 		goto done;
174 	}
175 	name = xop->head.name1;
176 	name_len = xop->head.name1_len;
177 
178 	/*
179 	 * Lookup the directory entry
180 	 */
181 	lhc = hammer2_dirhash(name, name_len);
182 	chain = hammer2_chain_lookup(&parent, &key_next,
183 				     lhc, lhc + HAMMER2_DIRHASH_LOMASK,
184 				     &cache_index,
185 				     HAMMER2_LOOKUP_ALWAYS |
186 				     HAMMER2_LOOKUP_SHARED);
187 	while (chain) {
188 		ripdata = &chain->data->ipdata;
189 		if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
190 		    ripdata->meta.name_len == name_len &&
191 		    bcmp(ripdata->filename, name, name_len) == 0) {
192 			break;
193 		}
194 		chain = hammer2_chain_next(&parent, chain, &key_next,
195 					   key_next,
196 					   lhc + HAMMER2_DIRHASH_LOMASK,
197 					   &cache_index,
198 					   HAMMER2_LOOKUP_ALWAYS |
199 					   HAMMER2_LOOKUP_SHARED);
200 	}
201 
202 	/*
203 	 * If the entry is a hardlink pointer, resolve it.
204 	 */
205 	error = 0;
206 	if (chain) {
207 		if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
208 			error = hammer2_chain_hardlink_find(
209 						xop->head.ip1,
210 						&parent, &chain,
211 						HAMMER2_RESOLVE_SHARED);
212 		}
213 	}
214 done:
215 	error = hammer2_xop_feed(&xop->head, chain, clindex, error);
216 	if (chain) {
217 		/* leave lock intact for feed */
218 		hammer2_chain_drop(chain);
219 	}
220 	if (parent) {
221 		hammer2_chain_unlock(parent);
222 		hammer2_chain_drop(parent);
223 	}
224 }
225 
226 /*
227  * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper
228  * for hammer2_vop_nrename().
229  *
230  * This function locates and removes the directory entry.  If the
231  * entry is a hardlink pointer, this function will also remove the
232  * hardlink target if the target's nlinks is 1.
233  *
234  * The frontend is responsible for moving open inodes to the hidden directory
235  * and for decrementing nlinks.
236  */
237 void
238 hammer2_xop_unlink(hammer2_xop_t *arg, int clindex)
239 {
240 	hammer2_xop_unlink_t *xop = &arg->xop_unlink;
241 	hammer2_chain_t *parent;
242 	hammer2_chain_t *chain;
243 	const hammer2_inode_data_t *ripdata;
244 	const char *name;
245 	size_t name_len;
246 	hammer2_key_t key_next;
247 	hammer2_key_t lhc;
248 	int cache_index = -1;	/* XXX */
249 	int error;
250 
251 	/*
252 	 * Requires exclusive lock
253 	 */
254 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
255 				     HAMMER2_RESOLVE_ALWAYS);
256 	if (parent == NULL) {
257 		kprintf("xop_nresolve: NULL parent\n");
258 		chain = NULL;
259 		error = EIO;
260 		goto done;
261 	}
262 	name = xop->head.name1;
263 	name_len = xop->head.name1_len;
264 
265 	/*
266 	 * Lookup the directory entry
267 	 */
268 	lhc = hammer2_dirhash(name, name_len);
269 	chain = hammer2_chain_lookup(&parent, &key_next,
270 				     lhc, lhc + HAMMER2_DIRHASH_LOMASK,
271 				     &cache_index,
272 				     HAMMER2_LOOKUP_ALWAYS);
273 	while (chain) {
274 		ripdata = &chain->data->ipdata;
275 		if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
276 		    ripdata->meta.name_len == name_len &&
277 		    bcmp(ripdata->filename, name, name_len) == 0) {
278 			break;
279 		}
280 		chain = hammer2_chain_next(&parent, chain, &key_next,
281 					   key_next,
282 					   lhc + HAMMER2_DIRHASH_LOMASK,
283 					   &cache_index,
284 					   HAMMER2_LOOKUP_ALWAYS);
285 	}
286 
287 	/*
288 	 * If the directory entry is a HARDLINK pointer then obtain the
289 	 * underlying file type for the directory typing tests and delete
290 	 * the HARDLINK pointer chain permanently.  The frontend is left
291 	 * responsible for handling nlinks on and deleting the actual inode.
292 	 *
293 	 * If the directory entry is the actual inode then use its type
294 	 * for the directory typing tests and delete the chain, permanency
295 	 * depends on whether the inode is open or not.
296 	 *
297 	 * Check directory typing and delete the entry.  Note that
298 	 * nlinks adjustments are made on the real inode by the frontend,
299 	 * not here.
300 	 */
301 	error = 0;
302 	if (chain) {
303 		int dopermanent = xop->dopermanent;
304 		uint8_t type;
305 
306 		type = chain->data->ipdata.meta.type;
307 		if (type == HAMMER2_OBJTYPE_HARDLINK) {
308 			type = chain->data->ipdata.meta.target_type;
309 			dopermanent |= HAMMER2_DELETE_PERMANENT;
310 		}
311 
312 		if (type == HAMMER2_OBJTYPE_DIRECTORY &&
313 		    xop->isdir == 0) {
314 			error = ENOTDIR;
315 		} else
316 		if (type != HAMMER2_OBJTYPE_DIRECTORY &&
317 		    xop->isdir >= 1) {
318 			error = EISDIR;
319 		} else {
320 			/*
321 			 * This deletes the directory entry itself, which is
322 			 * also the inode when nlinks == 1.  Hardlink targets
323 			 * are handled in the next conditional.
324 			 */
325 			hammer2_chain_delete(parent, chain,
326 					     xop->head.mtid, dopermanent);
327 		}
328 	}
329 
330 	/*
331 	 * If the entry is a hardlink pointer, resolve it.  If this is the
332 	 * last link, delete it.  The frontend has the master copy of nlinks
333 	 * but we still have to make adjustments here to synchronize with it.
334 	 *
335 	 * On delete / adjust nlinks if there is no error.  But we still need
336 	 * to resolve the hardlink to feed the inode's real chain back to
337 	 * the frontend.
338 	 *
339 	 * XXX we are basically tracking the nlinks count by doing a delta
340 	 *     adjustment instead of having the frontend pass the absolute
341 	 *     value down.  We really need to have the frontend pass the
342 	 *     absolute value down (difficult because there might not be
343 	 *     an 'ip').  See also hammer2_xop_nlink().
344 	 */
345 	if (chain &&
346 	    chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
347 		int error2;
348 
349 		error2 = hammer2_chain_hardlink_find(xop->head.ip1,
350 						     &parent, &chain, 0);
351 		if (chain && error == 0 && error2 == 0 &&
352 		    (int64_t)chain->data->ipdata.meta.nlinks <= 1) {
353 			hammer2_chain_delete(parent, chain,
354 					     xop->head.mtid,
355 					     xop->dopermanent);
356 		} else if (chain && error == 0 && error2 == 0) {
357 			hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
358 			--chain->data->ipdata.meta.nlinks;
359 		}
360 		if (error == 0)
361 			error = error2;
362 	}
363 
364 	/*
365 	 * Chains passed to feed are expected to be locked shared.
366 	 */
367 	if (chain) {
368 		hammer2_chain_unlock(chain);
369 		hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS |
370 					  HAMMER2_RESOLVE_SHARED);
371 	}
372 
373 	/*
374 	 * We always return the hardlink target (the real inode) for
375 	 * further action.
376 	 */
377 done:
378 	hammer2_xop_feed(&xop->head, chain, clindex, error);
379 	if (chain)
380 		hammer2_chain_drop(chain);
381 	if (parent) {
382 		hammer2_chain_unlock(parent);
383 		hammer2_chain_drop(parent);
384 	}
385 }
386 
387 /*
388  * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
389  *
390  * ip1 - fdip
391  * ip2 - ip
392  * ip3 - cdip
393  *
394  * If a hardlink pointer:
395  *	The existing hardlink target {fdip,ip} must be moved to another
396  *	directory {cdip,ip}
397  *
398  * If not a hardlink pointer:
399  *	Convert the target {fdip,ip} to a hardlink target {cdip,ip} and
400  *	replace the original namespace {fdip,name} with a hardlink pointer.
401  */
402 void
403 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex)
404 {
405 	hammer2_xop_nlink_t *xop = &arg->xop_nlink;
406 	hammer2_pfs_t *pmp;
407 	hammer2_inode_data_t *wipdata;
408 	hammer2_chain_t *parent;
409 	hammer2_chain_t *chain;
410 	hammer2_chain_t *tmp;
411 	hammer2_inode_t *ip;
412 	hammer2_key_t key_dummy;
413 	int cache_index = -1;
414 	int error;
415 	int did_delete = 0;
416 
417 	/*
418 	 * We need the precise parent chain to issue the deletion.
419 	 */
420 	ip = xop->head.ip2;
421 	pmp = ip->pmp;
422 	parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
423 	if (parent)
424 		hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
425 	if (parent == NULL) {
426 		chain = NULL;
427 		error = EIO;
428 		goto done;
429 	}
430 	chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
431 	if (chain == NULL) {
432 		error = EIO;
433 		goto done;
434 	}
435 	KKASSERT(chain->parent == parent);
436 
437 	if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
438 		/*
439 		 * Delete the original chain and hold onto it for the move
440 		 * to cdir.
441 		 *
442 		 * Replace the namespace with a hardlink pointer if the
443 		 * chain being moved is not already a hardlink target.
444 		 */
445 		hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
446 		did_delete = 1;
447 
448 		tmp = NULL;
449 		error = hammer2_chain_create(&parent, &tmp, pmp,
450 					     chain->bref.key, 0,
451 					     HAMMER2_BREF_TYPE_INODE,
452 					     HAMMER2_INODE_BYTES,
453 					     xop->head.mtid, 0, 0);
454 		if (error)
455 			goto done;
456 		hammer2_chain_modify(tmp, xop->head.mtid, 0, 0);
457 		wipdata = &tmp->data->ipdata;
458 		bzero(wipdata, sizeof(*wipdata));
459 		wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
460 		wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
461 		bcopy(chain->data->ipdata.filename, wipdata->filename,
462 		      chain->data->ipdata.meta.name_len);
463 		wipdata->meta.target_type = chain->data->ipdata.meta.type;
464 		wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
465 		wipdata->meta.inum = ip->meta.inum;
466 		wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
467 		wipdata->meta.nlinks = 1;
468 		wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
469 
470 		hammer2_chain_unlock(tmp);
471 		hammer2_chain_drop(tmp);
472 	} else if (xop->head.ip1 != xop->head.ip3) {
473 		/*
474 		 * Delete the hardlink target so it can be moved
475 		 * to cdir.
476 		 */
477 		hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
478 		did_delete = 1;
479 	} else {
480 		/*
481 		 * Deletion not necessary (just a nlinks update).
482 		 */
483 		did_delete = 0;
484 	}
485 
486 	hammer2_chain_unlock(parent);
487 	hammer2_chain_drop(parent);
488 	parent = NULL;
489 
490 	/*
491 	 * Ok, back to the deleted chain.  We must reconnect this chain
492 	 * as a hardlink target to cdir (ip3).
493 	 *
494 	 * WARNING! Frontend assumes filename length is 18 bytes.
495 	 */
496 	if (did_delete) {
497 		hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
498 		wipdata = &chain->data->ipdata;
499 		ksnprintf(wipdata->filename, sizeof(wipdata->filename),
500 			  "0x%016jx", (intmax_t)ip->meta.inum);
501 		wipdata->meta.name_len = strlen(wipdata->filename);
502 		wipdata->meta.name_key = ip->meta.inum;
503 
504 		/*
505 		 * We must seek parent properly for the create to reattach
506 		 * chain.  XXX just use chain->parent or
507 		 * inode_chain_and_parent() ?
508 		 */
509 		parent = hammer2_inode_chain(xop->head.ip3, clindex,
510 					     HAMMER2_RESOLVE_ALWAYS);
511 		if (parent == NULL) {
512 			error = EIO;
513 			goto done;
514 		}
515 		tmp = hammer2_chain_lookup(&parent, &key_dummy,
516 					   ip->meta.inum, ip->meta.inum,
517 					   &cache_index, 0);
518 		if (tmp) {
519 			hammer2_chain_unlock(tmp);
520 			hammer2_chain_drop(tmp);
521 			error = EEXIST;
522 			goto done;
523 		}
524 		error = hammer2_chain_create(&parent, &chain, pmp,
525 					     wipdata->meta.name_key, 0,
526 					     HAMMER2_BREF_TYPE_INODE,
527 					     HAMMER2_INODE_BYTES,
528 					     xop->head.mtid, 0, 0);
529 	} else {
530 		error = 0;
531 	}
532 
533 	/*
534 	 * Bump nlinks to synchronize with frontend.
535 	 */
536 	if (xop->nlinks_delta) {
537 		hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
538 		chain->data->ipdata.meta.nlinks += xop->nlinks_delta;
539 	}
540 
541 	/*
542 	 * To avoid having to scan the collision space we can simply
543 	 * reuse the inode's original name_key.  But ip->meta.name_key
544 	 * may have already been updated by the front-end, so use xop->lhc.
545 	 *
546 	 * (frontend is responsible for fixing up ip->pip).
547 	 */
548 done:
549 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
550 	if (parent) {
551 		hammer2_chain_unlock(parent);
552 		hammer2_chain_drop(parent);
553 	}
554 	if (chain) {
555 		hammer2_chain_unlock(chain);
556 		hammer2_chain_drop(chain);
557 	}
558 }
559 
560 /*
561  * Backend for hammer2_vop_nrename()
562  *
563  * This handles the final step of renaming, either renaming the
564  * actual inode or renaming the hardlink pointer.
565  */
566 void
567 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
568 {
569 	hammer2_xop_nrename_t *xop = &arg->xop_nrename;
570 	hammer2_pfs_t *pmp;
571 	hammer2_chain_t *parent;
572 	hammer2_chain_t *chain;
573 	hammer2_chain_t *tmp;
574 	hammer2_inode_t *ip;
575 	hammer2_key_t key_dummy;
576 	int cache_index = -1;
577 	int error;
578 
579 	/*
580 	 * We need the precise parent chain to issue the deletion.
581 	 *
582 	 * If this is not a hardlink target we can act on the inode,
583 	 * otherwise we have to locate the hardlink pointer.
584 	 */
585 	ip = xop->head.ip2;
586 	pmp = ip->pmp;
587 	chain = NULL;
588 
589 	if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
590 		/*
591 		 * Find ip's direct parent chain.
592 		 */
593 		parent = hammer2_inode_chain(ip, clindex,
594 					     HAMMER2_RESOLVE_ALWAYS);
595 		if (parent)
596 			hammer2_chain_getparent(&parent,
597 						HAMMER2_RESOLVE_ALWAYS);
598 		if (parent == NULL) {
599 			error = EIO;
600 			goto done;
601 		}
602 		chain = hammer2_inode_chain(ip, clindex,
603 					    HAMMER2_RESOLVE_ALWAYS);
604 		if (chain == NULL) {
605 			error = EIO;
606 			goto done;
607 		}
608 	} else {
609 		/*
610 		 * The hardlink pointer for the head.ip1 hardlink target
611 		 * is in fdip, do a namespace search.
612 		 */
613 		const hammer2_inode_data_t *ripdata;
614 		hammer2_key_t lhc;
615 		hammer2_key_t key_next;
616 		const char *name;
617 		size_t name_len;
618 
619 		parent = hammer2_inode_chain(xop->head.ip1, clindex,
620 					     HAMMER2_RESOLVE_ALWAYS);
621 		if (parent == NULL) {
622 			kprintf("xop_nrename: NULL parent\n");
623 			error = EIO;
624 			goto done;
625 		}
626 		name = xop->head.name1;
627 		name_len = xop->head.name1_len;
628 
629 		/*
630 		 * Lookup the directory entry
631 		 */
632 		lhc = hammer2_dirhash(name, name_len);
633 		chain = hammer2_chain_lookup(&parent, &key_next,
634 					     lhc, lhc + HAMMER2_DIRHASH_LOMASK,
635 					     &cache_index,
636 					     HAMMER2_LOOKUP_ALWAYS);
637 		while (chain) {
638 			ripdata = &chain->data->ipdata;
639 			if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
640 			    ripdata->meta.name_len == name_len &&
641 			    bcmp(ripdata->filename, name, name_len) == 0) {
642 				break;
643 			}
644 			chain = hammer2_chain_next(&parent, chain, &key_next,
645 						   key_next,
646 						   lhc + HAMMER2_DIRHASH_LOMASK,
647 						   &cache_index,
648 						   HAMMER2_LOOKUP_ALWAYS);
649 		}
650 	}
651 
652 	if (chain == NULL) {
653 		/* XXX shouldn't happen, but does under fsstress */
654 		kprintf("hammer2_xop_rename: \"%s\" -> \"%s\"  ENOENT\n",
655 			xop->head.name1,
656 			xop->head.name2);
657 		error = ENOENT;
658 		goto done;
659 	}
660 
661 	/*
662 	 * Delete it, then create it in the new namespace.
663 	 */
664 	hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
665 	hammer2_chain_unlock(parent);
666 	hammer2_chain_drop(parent);
667 	parent = NULL;		/* safety */
668 
669 	/*
670 	 * Ok, back to the deleted chain.  We must reconnect this chain
671 	 * to tdir (ip3).  The chain (a real inode or a hardlink pointer)
672 	 * is not otherwise modified.
673 	 *
674 	 * Frontend is expected to replicate the same inode meta data
675 	 * modifications.
676 	 *
677 	 * NOTE!  This chain may not represent the actual inode, it
678 	 *	  can be a hardlink pointer.
679 	 *
680 	 * XXX in-inode parent directory specification?
681 	 */
682 	if (chain->data->ipdata.meta.name_key != xop->lhc ||
683 	    xop->head.name1_len != xop->head.name2_len ||
684 	    bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
685 		hammer2_inode_data_t *wipdata;
686 
687 		hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
688 		wipdata = &chain->data->ipdata;
689 
690 		bzero(wipdata->filename, sizeof(wipdata->filename));
691 		bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
692 		wipdata->meta.name_key = xop->lhc;
693 		wipdata->meta.name_len = xop->head.name2_len;
694 	}
695 
696 	/*
697 	 * We must seek parent properly for the create.
698 	 */
699 	parent = hammer2_inode_chain(xop->head.ip3, clindex,
700 				     HAMMER2_RESOLVE_ALWAYS);
701 	if (parent == NULL) {
702 		error = EIO;
703 		goto done;
704 	}
705 	tmp = hammer2_chain_lookup(&parent, &key_dummy,
706 				   xop->lhc, xop->lhc,
707 				   &cache_index, 0);
708 	if (tmp) {
709 		hammer2_chain_unlock(tmp);
710 		hammer2_chain_drop(tmp);
711 		error = EEXIST;
712 		goto done;
713 	}
714 
715 	error = hammer2_chain_create(&parent, &chain, pmp,
716 				     xop->lhc, 0,
717 				     HAMMER2_BREF_TYPE_INODE,
718 				     HAMMER2_INODE_BYTES,
719 				     xop->head.mtid, 0, 0);
720 	/*
721 	 * (frontend is responsible for fixing up ip->pip).
722 	 */
723 done:
724 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
725 	if (parent) {
726 		hammer2_chain_unlock(parent);
727 		hammer2_chain_drop(parent);
728 	}
729 	if (chain) {
730 		hammer2_chain_unlock(chain);
731 		hammer2_chain_drop(chain);
732 	}
733 }
734 
735 /*
736  * Directory collision resolver scan helper (backend, threaded).
737  *
738  * Used by the inode create code to locate an unused lhc.
739  */
740 void
741 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex)
742 {
743 	hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
744 	hammer2_chain_t *parent;
745 	hammer2_chain_t *chain;
746 	hammer2_key_t key_next;
747 	int cache_index = -1;	/* XXX */
748 	int error = 0;
749 
750 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
751 				     HAMMER2_RESOLVE_ALWAYS |
752 				     HAMMER2_RESOLVE_SHARED);
753 	if (parent == NULL) {
754 		kprintf("xop_nresolve: NULL parent\n");
755 		chain = NULL;
756 		error = EIO;
757 		goto done;
758 	}
759 
760 	/*
761 	 * Lookup all possibly conflicting directory entries, the feed
762 	 * inherits the chain's lock so do not unlock it on the iteration.
763 	 */
764 	chain = hammer2_chain_lookup(&parent, &key_next,
765 				     xop->lhc,
766 				     xop->lhc + HAMMER2_DIRHASH_LOMASK,
767 				     &cache_index,
768 				     HAMMER2_LOOKUP_ALWAYS |
769 				     HAMMER2_LOOKUP_SHARED);
770 	while (chain) {
771 		error = hammer2_xop_feed(&xop->head, chain, clindex,
772 					 chain->error);
773 		if (error) {
774 			hammer2_chain_drop(chain);
775 			chain = NULL;	/* safety */
776 			break;
777 		}
778 		chain = hammer2_chain_next(&parent, chain, &key_next,
779 					   key_next,
780 					   xop->lhc + HAMMER2_DIRHASH_LOMASK,
781 					   &cache_index,
782 					   HAMMER2_LOOKUP_ALWAYS |
783 					   HAMMER2_LOOKUP_SHARED |
784 					   HAMMER2_LOOKUP_NOUNLOCK);
785 	}
786 done:
787 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
788 	if (parent) {
789 		hammer2_chain_unlock(parent);
790 		hammer2_chain_drop(parent);
791 	}
792 }
793 
794 /*
795  * Generic lookup of a specific key.
796  *
797  * Used by the inode hidden directory code to find the hidden directory.
798  */
799 void
800 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex)
801 {
802 	hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
803 	hammer2_chain_t *parent;
804 	hammer2_chain_t *chain;
805 	hammer2_key_t key_next;
806 	int cache_index = -1;	/* XXX */
807 	int error = 0;
808 
809 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
810 				     HAMMER2_RESOLVE_ALWAYS |
811 				     HAMMER2_RESOLVE_SHARED);
812 	chain = NULL;
813 	if (parent == NULL) {
814 		error = EIO;
815 		goto done;
816 	}
817 
818 	/*
819 	 * Lookup all possibly conflicting directory entries, the feed
820 	 * inherits the chain's lock so do not unlock it on the iteration.
821 	 */
822 	chain = hammer2_chain_lookup(&parent, &key_next,
823 				     xop->lhc, xop->lhc,
824 				     &cache_index,
825 				     HAMMER2_LOOKUP_ALWAYS |
826 				     HAMMER2_LOOKUP_SHARED);
827 	if (chain)
828 		hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
829 	else
830 		hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT);
831 
832 done:
833 	if (chain) {
834 		/* leave lock intact for feed */
835 		hammer2_chain_drop(chain);
836 	}
837 	if (parent) {
838 		hammer2_chain_unlock(parent);
839 		hammer2_chain_drop(parent);
840 	}
841 }
842 
843 /*
844  * Generic scan
845  */
846 void
847 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex)
848 {
849 	hammer2_xop_scanall_t *xop = &arg->xop_scanall;
850 	hammer2_chain_t *parent;
851 	hammer2_chain_t *chain;
852 	hammer2_key_t key_next;
853 	int cache_index = -1;
854 	int error = 0;
855 
856 	/*
857 	 * The inode's chain is the iterator.  If we cannot acquire it our
858 	 * contribution ends here.
859 	 */
860 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
861 				     HAMMER2_RESOLVE_ALWAYS |
862 				     HAMMER2_RESOLVE_SHARED);
863 	if (parent == NULL) {
864 		kprintf("xop_readdir: NULL parent\n");
865 		goto done;
866 	}
867 
868 	/*
869 	 * Generic scan of exact records.  Note that indirect blocks are
870 	 * automatically recursed and will not be returned.
871 	 */
872 	chain = hammer2_chain_lookup(&parent, &key_next,
873 				     xop->key_beg, xop->key_end,
874 				     &cache_index, HAMMER2_LOOKUP_SHARED |
875 						   HAMMER2_LOOKUP_NODIRECT);
876 	while (chain) {
877 		error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
878 		if (error)
879 			break;
880 		chain = hammer2_chain_next(&parent, chain, &key_next,
881 					   key_next, xop->key_end,
882 					   &cache_index,
883 					   HAMMER2_LOOKUP_SHARED |
884 					   HAMMER2_LOOKUP_NODIRECT |
885 					   HAMMER2_LOOKUP_NOUNLOCK);
886 	}
887 	if (chain)
888 		hammer2_chain_drop(chain);
889 	hammer2_chain_unlock(parent);
890 	hammer2_chain_drop(parent);
891 done:
892 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
893 }
894