xref: /dragonfly/sys/vfs/hammer2/hammer2_xops.c (revision 00cac4e7)
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  * Determine if the specified directory is empty.  Returns 0 on success.
65  */
66 static
67 int
68 checkdirempty(hammer2_chain_t *orig_parent)
69 {
70 	hammer2_chain_t *parent;
71 	hammer2_chain_t *chain;
72 	hammer2_key_t key_next;
73 	int cache_index = -1;
74 	int error;
75 
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);
85 	}
86 	hammer2_chain_lookup_done(parent);
87 
88 	return error;
89 }
90 
91 /*
92  * Backend for hammer2_vfs_root()
93  *
94  * This is called when a newly mounted PFS has not yet synchronized
95  * to the inode_tid and modify_tid.
96  */
97 void
98 hammer2_xop_ipcluster(hammer2_xop_t *arg, int clindex)
99 {
100 	hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster;
101 	hammer2_chain_t *chain;
102 	int error;
103 
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;
111 
112 	hammer2_xop_feed(&xop->head, chain, clindex, error);
113 	if (chain) {
114 		hammer2_chain_unlock(chain);
115 		hammer2_chain_drop(chain);
116 	}
117 }
118 
119 /*
120  * Backend for hammer2_vop_readdir()
121  */
122 void
123 hammer2_xop_readdir(hammer2_xop_t *arg, int clindex)
124 {
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;
132 
133 	lkey = xop->lkey;
134 	if (hammer2_debug & 0x0020)
135 		kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
136 
137 	/*
138 	 * The inode's chain is the iterator.  If we cannot acquire it our
139 	 * contribution ends here.
140 	 */
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;
147 	}
148 
149 	/*
150 	 * Directory scan [re]start and loop, the feed inherits the chain's
151 	 * lock so do not unlock it on the iteration.
152 	 */
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);
160 	}
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);
169 	}
170 	if (chain) {
171 		hammer2_chain_unlock(chain);
172 		hammer2_chain_drop(chain);
173 	}
174 	hammer2_chain_unlock(parent);
175 	hammer2_chain_drop(parent);
176 done:
177 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
178 }
179 
180 /*
181  * Backend for hammer2_vop_nresolve()
182  */
183 void
184 hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex)
185 {
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;
196 
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;
205 	}
206 	name = xop->head.name1;
207 	name_len = xop->head.name1_len;
208 
209 	/*
210 	 * Lookup the directory entry
211 	 */
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;
224 		}
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);
231 	}
232 
233 	/*
234 	 * If the entry is a hardlink pointer, resolve it.
235 	 */
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);
243 		}
244 	}
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);
250 	}
251 	if (parent) {
252 		hammer2_chain_unlock(parent);
253 		hammer2_chain_drop(parent);
254 	}
255 }
256 
257 /*
258  * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper
259  * for hammer2_vop_nrename().
260  *
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.
264  *
265  * The frontend is responsible for moving open inodes to the hidden directory
266  * and for decrementing nlinks.
267  */
268 void
269 hammer2_xop_unlink(hammer2_xop_t *arg, int clindex)
270 {
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;
281 
282 	/*
283 	 * Requires exclusive lock
284 	 */
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;
292 	}
293 	name = xop->head.name1;
294 	name_len = xop->head.name1_len;
295 
296 	/*
297 	 * Lookup the directory entry
298 	 */
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;
310 		}
311 		chain = hammer2_chain_next(&parent, chain, &key_next,
312 					   key_next,
313 					   lhc + HAMMER2_DIRHASH_LOMASK,
314 					   &cache_index,
315 					   HAMMER2_LOOKUP_ALWAYS);
316 	}
317 
318 	/*
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.
323 	 *
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.
327 	 *
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.
331 	 */
332 	error = 0;
333 	if (chain) {
334 		int dopermanent = xop->dopermanent;
335 		uint8_t type;
336 
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;
341 		}
342 
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 {
354 			/*
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.
358 			 */
359 			error = chain->error;
360 			hammer2_chain_delete(parent, chain,
361 					     xop->head.mtid, dopermanent);
362 		}
363 	}
364 
365 	/*
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.
369 	 *
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.
373 	 *
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().
379 	 */
380 	if (chain &&
381 	    chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
382 		int error2;
383 
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;
394 		}
395 		if (error == 0)
396 			error = error2;
397 	}
398 
399 	/*
400 	 * Chains passed to feed are expected to be locked shared.
401 	 */
402 	if (chain)
403 		hammer2_chain_lock_downgrade(chain);
404 
405 	/*
406 	 * We always return the hardlink target (the real inode) for
407 	 * further action.
408 	 */
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;
415 	}
416 	if (parent) {
417 		hammer2_chain_unlock(parent);
418 		hammer2_chain_drop(parent);
419 		parent = NULL;
420 	}
421 }
422 
423 /*
424  * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
425  *
426  * ip1 - fdip
427  * ip2 - ip
428  * ip3 - cdip
429  *
430  * If a hardlink pointer:
431  *	The existing hardlink target {fdip,ip} must be moved to another
432  *	directory {cdip,ip}
433  *
434  * If not a hardlink pointer:
435  *	Convert the target {fdip,ip} to a hardlink target {cdip,ip} and
436  *	replace the original namespace {fdip,name} with a hardlink pointer.
437  */
438 void
439 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex)
440 {
441 	hammer2_xop_nlink_t *xop = &arg->xop_nlink;
442 	hammer2_pfs_t *pmp;
443 	hammer2_inode_data_t *wipdata;
444 	hammer2_chain_t *parent;
445 	hammer2_chain_t *chain;
446 	hammer2_chain_t *tmp;
447 	hammer2_inode_t *ip;
448 	hammer2_key_t key_dummy;
449 	int cache_index = -1;
450 	int error;
451 	int did_delete = 0;
452 
453 	/*
454 	 * We need the precise parent chain to issue the deletion.
455 	 */
456 	ip = xop->head.ip2;
457 	pmp = ip->pmp;
458 	parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
459 	if (parent)
460 		hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
461 	if (parent == NULL) {
462 		chain = NULL;
463 		error = EIO;
464 		goto done;
465 	}
466 	chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
467 	if (chain == NULL) {
468 		error = EIO;
469 		goto done;
470 	}
471 	KKASSERT(chain->parent == parent);
472 
473 	if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
474 		/*
475 		 * Delete the original chain and hold onto it for the move
476 		 * to cdir.
477 		 *
478 		 * Replace the namespace with a hardlink pointer if the
479 		 * chain being moved is not already a hardlink target.
480 		 */
481 		hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
482 		did_delete = 1;
483 
484 		tmp = NULL;
485 		error = hammer2_chain_create(&parent, &tmp, pmp,
486 					     chain->bref.key, 0,
487 					     HAMMER2_BREF_TYPE_INODE,
488 					     HAMMER2_INODE_BYTES,
489 					     xop->head.mtid, 0, 0);
490 		if (error)
491 			goto done;
492 		hammer2_chain_modify(tmp, xop->head.mtid, 0, 0);
493 		wipdata = &tmp->data->ipdata;
494 		bzero(wipdata, sizeof(*wipdata));
495 		wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
496 		wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
497 		bcopy(chain->data->ipdata.filename, wipdata->filename,
498 		      chain->data->ipdata.meta.name_len);
499 		wipdata->meta.target_type = chain->data->ipdata.meta.type;
500 		wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
501 		wipdata->meta.inum = ip->meta.inum;
502 		wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
503 		wipdata->meta.nlinks = 1;
504 		wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
505 
506 		hammer2_chain_unlock(tmp);
507 		hammer2_chain_drop(tmp);
508 	} else if (xop->head.ip1 != xop->head.ip3) {
509 		/*
510 		 * Delete the hardlink target so it can be moved
511 		 * to cdir.
512 		 */
513 		hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
514 		did_delete = 1;
515 	} else {
516 		/*
517 		 * Deletion not necessary (just a nlinks update).
518 		 */
519 		did_delete = 0;
520 	}
521 
522 	hammer2_chain_unlock(parent);
523 	hammer2_chain_drop(parent);
524 	parent = NULL;
525 
526 	/*
527 	 * Ok, back to the deleted chain.  We must reconnect this chain
528 	 * as a hardlink target to cdir (ip3).
529 	 *
530 	 * WARNING! Frontend assumes filename length is 18 bytes.
531 	 */
532 	if (did_delete) {
533 		hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
534 		wipdata = &chain->data->ipdata;
535 		ksnprintf(wipdata->filename, sizeof(wipdata->filename),
536 			  "0x%016jx", (intmax_t)ip->meta.inum);
537 		wipdata->meta.name_len = strlen(wipdata->filename);
538 		wipdata->meta.name_key = ip->meta.inum;
539 
540 		/*
541 		 * We must seek parent properly for the create to reattach
542 		 * chain.  XXX just use chain->parent or
543 		 * inode_chain_and_parent() ?
544 		 */
545 		parent = hammer2_inode_chain(xop->head.ip3, clindex,
546 					     HAMMER2_RESOLVE_ALWAYS);
547 		if (parent == NULL) {
548 			error = EIO;
549 			goto done;
550 		}
551 		tmp = hammer2_chain_lookup(&parent, &key_dummy,
552 					   ip->meta.inum, ip->meta.inum,
553 					   &cache_index, 0);
554 		if (tmp) {
555 			hammer2_chain_unlock(tmp);
556 			hammer2_chain_drop(tmp);
557 			error = EEXIST;
558 			goto done;
559 		}
560 		error = hammer2_chain_create(&parent, &chain, pmp,
561 					     wipdata->meta.name_key, 0,
562 					     HAMMER2_BREF_TYPE_INODE,
563 					     HAMMER2_INODE_BYTES,
564 					     xop->head.mtid, 0, 0);
565 	} else {
566 		error = 0;
567 	}
568 
569 	/*
570 	 * Bump nlinks to synchronize with frontend.
571 	 */
572 	if (xop->nlinks_delta) {
573 		hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
574 		chain->data->ipdata.meta.nlinks += xop->nlinks_delta;
575 	}
576 
577 	/*
578 	 * To avoid having to scan the collision space we can simply
579 	 * reuse the inode's original name_key.  But ip->meta.name_key
580 	 * may have already been updated by the front-end, so use xop->lhc.
581 	 *
582 	 * (frontend is responsible for fixing up ip->pip).
583 	 */
584 done:
585 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
586 	if (parent) {
587 		hammer2_chain_unlock(parent);
588 		hammer2_chain_drop(parent);
589 	}
590 	if (chain) {
591 		hammer2_chain_unlock(chain);
592 		hammer2_chain_drop(chain);
593 	}
594 }
595 
596 /*
597  * Backend for hammer2_vop_nrename()
598  *
599  * This handles the final step of renaming, either renaming the
600  * actual inode or renaming the hardlink pointer.
601  */
602 void
603 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
604 {
605 	hammer2_xop_nrename_t *xop = &arg->xop_nrename;
606 	hammer2_pfs_t *pmp;
607 	hammer2_chain_t *parent;
608 	hammer2_chain_t *chain;
609 	hammer2_chain_t *tmp;
610 	hammer2_inode_t *ip;
611 	hammer2_key_t key_dummy;
612 	int cache_index = -1;
613 	int error;
614 
615 	/*
616 	 * We need the precise parent chain to issue the deletion.
617 	 *
618 	 * If this is not a hardlink target we can act on the inode,
619 	 * otherwise we have to locate the hardlink pointer.
620 	 */
621 	ip = xop->head.ip2;
622 	pmp = ip->pmp;
623 	chain = NULL;
624 
625 	if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
626 		/*
627 		 * Find ip's direct parent chain.
628 		 */
629 		parent = hammer2_inode_chain(ip, clindex,
630 					     HAMMER2_RESOLVE_ALWAYS);
631 		if (parent)
632 			hammer2_chain_getparent(&parent,
633 						HAMMER2_RESOLVE_ALWAYS);
634 		if (parent == NULL) {
635 			error = EIO;
636 			goto done;
637 		}
638 		chain = hammer2_inode_chain(ip, clindex,
639 					    HAMMER2_RESOLVE_ALWAYS);
640 		if (chain == NULL) {
641 			error = EIO;
642 			goto done;
643 		}
644 	} else {
645 		/*
646 		 * The hardlink pointer for the head.ip1 hardlink target
647 		 * is in fdip, do a namespace search.
648 		 */
649 		const hammer2_inode_data_t *ripdata;
650 		hammer2_key_t lhc;
651 		hammer2_key_t key_next;
652 		const char *name;
653 		size_t name_len;
654 
655 		parent = hammer2_inode_chain(xop->head.ip1, clindex,
656 					     HAMMER2_RESOLVE_ALWAYS);
657 		if (parent == NULL) {
658 			kprintf("xop_nrename: NULL parent\n");
659 			error = EIO;
660 			goto done;
661 		}
662 		name = xop->head.name1;
663 		name_len = xop->head.name1_len;
664 
665 		/*
666 		 * Lookup the directory entry
667 		 */
668 		lhc = hammer2_dirhash(name, name_len);
669 		chain = hammer2_chain_lookup(&parent, &key_next,
670 					     lhc, lhc + HAMMER2_DIRHASH_LOMASK,
671 					     &cache_index,
672 					     HAMMER2_LOOKUP_ALWAYS);
673 		while (chain) {
674 			ripdata = &chain->data->ipdata;
675 			if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
676 			    ripdata->meta.name_len == name_len &&
677 			    bcmp(ripdata->filename, name, name_len) == 0) {
678 				break;
679 			}
680 			chain = hammer2_chain_next(&parent, chain, &key_next,
681 						   key_next,
682 						   lhc + HAMMER2_DIRHASH_LOMASK,
683 						   &cache_index,
684 						   HAMMER2_LOOKUP_ALWAYS);
685 		}
686 	}
687 
688 	if (chain == NULL) {
689 		/* XXX shouldn't happen, but does under fsstress */
690 		kprintf("hammer2_xop_rename: \"%s\" -> \"%s\"  ENOENT\n",
691 			xop->head.name1,
692 			xop->head.name2);
693 		error = ENOENT;
694 		goto done;
695 	}
696 
697 	/*
698 	 * Delete it, then create it in the new namespace.
699 	 */
700 	hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
701 	hammer2_chain_unlock(parent);
702 	hammer2_chain_drop(parent);
703 	parent = NULL;		/* safety */
704 
705 	/*
706 	 * Ok, back to the deleted chain.  We must reconnect this chain
707 	 * to tdir (ip3).  The chain (a real inode or a hardlink pointer)
708 	 * is not otherwise modified.
709 	 *
710 	 * Frontend is expected to replicate the same inode meta data
711 	 * modifications.
712 	 *
713 	 * NOTE!  This chain may not represent the actual inode, it
714 	 *	  can be a hardlink pointer.
715 	 *
716 	 * XXX in-inode parent directory specification?
717 	 */
718 	if (chain->data->ipdata.meta.name_key != xop->lhc ||
719 	    xop->head.name1_len != xop->head.name2_len ||
720 	    bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
721 		hammer2_inode_data_t *wipdata;
722 
723 		hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
724 		wipdata = &chain->data->ipdata;
725 
726 		bzero(wipdata->filename, sizeof(wipdata->filename));
727 		bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
728 		wipdata->meta.name_key = xop->lhc;
729 		wipdata->meta.name_len = xop->head.name2_len;
730 	}
731 
732 	/*
733 	 * We must seek parent properly for the create.
734 	 */
735 	parent = hammer2_inode_chain(xop->head.ip3, clindex,
736 				     HAMMER2_RESOLVE_ALWAYS);
737 	if (parent == NULL) {
738 		error = EIO;
739 		goto done;
740 	}
741 	tmp = hammer2_chain_lookup(&parent, &key_dummy,
742 				   xop->lhc, xop->lhc,
743 				   &cache_index, 0);
744 	if (tmp) {
745 		hammer2_chain_unlock(tmp);
746 		hammer2_chain_drop(tmp);
747 		error = EEXIST;
748 		goto done;
749 	}
750 
751 	error = hammer2_chain_create(&parent, &chain, pmp,
752 				     xop->lhc, 0,
753 				     HAMMER2_BREF_TYPE_INODE,
754 				     HAMMER2_INODE_BYTES,
755 				     xop->head.mtid, 0, 0);
756 	/*
757 	 * (frontend is responsible for fixing up ip->pip).
758 	 */
759 done:
760 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
761 	if (parent) {
762 		hammer2_chain_unlock(parent);
763 		hammer2_chain_drop(parent);
764 	}
765 	if (chain) {
766 		hammer2_chain_unlock(chain);
767 		hammer2_chain_drop(chain);
768 	}
769 }
770 
771 /*
772  * Directory collision resolver scan helper (backend, threaded).
773  *
774  * Used by the inode create code to locate an unused lhc.
775  */
776 void
777 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex)
778 {
779 	hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
780 	hammer2_chain_t *parent;
781 	hammer2_chain_t *chain;
782 	hammer2_key_t key_next;
783 	int cache_index = -1;	/* XXX */
784 	int error = 0;
785 
786 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
787 				     HAMMER2_RESOLVE_ALWAYS |
788 				     HAMMER2_RESOLVE_SHARED);
789 	if (parent == NULL) {
790 		kprintf("xop_nresolve: NULL parent\n");
791 		chain = NULL;
792 		error = EIO;
793 		goto done;
794 	}
795 
796 	/*
797 	 * Lookup all possibly conflicting directory entries, the feed
798 	 * inherits the chain's lock so do not unlock it on the iteration.
799 	 */
800 	chain = hammer2_chain_lookup(&parent, &key_next,
801 				     xop->lhc,
802 				     xop->lhc + HAMMER2_DIRHASH_LOMASK,
803 				     &cache_index,
804 				     HAMMER2_LOOKUP_ALWAYS |
805 				     HAMMER2_LOOKUP_SHARED);
806 	while (chain) {
807 		error = hammer2_xop_feed(&xop->head, chain, clindex,
808 					 chain->error);
809 		if (error) {
810 			hammer2_chain_unlock(chain);
811 			hammer2_chain_drop(chain);
812 			chain = NULL;	/* safety */
813 			break;
814 		}
815 		chain = hammer2_chain_next(&parent, chain, &key_next,
816 					   key_next,
817 					   xop->lhc + HAMMER2_DIRHASH_LOMASK,
818 					   &cache_index,
819 					   HAMMER2_LOOKUP_ALWAYS |
820 					   HAMMER2_LOOKUP_SHARED);
821 	}
822 done:
823 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
824 	if (parent) {
825 		hammer2_chain_unlock(parent);
826 		hammer2_chain_drop(parent);
827 	}
828 }
829 
830 /*
831  * Generic lookup of a specific key.
832  *
833  * Used by the inode hidden directory code to find the hidden directory.
834  */
835 void
836 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex)
837 {
838 	hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
839 	hammer2_chain_t *parent;
840 	hammer2_chain_t *chain;
841 	hammer2_key_t key_next;
842 	int cache_index = -1;	/* XXX */
843 	int error = 0;
844 
845 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
846 				     HAMMER2_RESOLVE_ALWAYS |
847 				     HAMMER2_RESOLVE_SHARED);
848 	chain = NULL;
849 	if (parent == NULL) {
850 		error = EIO;
851 		goto done;
852 	}
853 
854 	/*
855 	 * Lookup all possibly conflicting directory entries, the feed
856 	 * inherits the chain's lock so do not unlock it on the iteration.
857 	 */
858 	chain = hammer2_chain_lookup(&parent, &key_next,
859 				     xop->lhc, xop->lhc,
860 				     &cache_index,
861 				     HAMMER2_LOOKUP_ALWAYS |
862 				     HAMMER2_LOOKUP_SHARED);
863 	if (chain)
864 		hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
865 	else
866 		hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT);
867 
868 done:
869 	if (chain) {
870 		hammer2_chain_unlock(chain);
871 		hammer2_chain_drop(chain);
872 	}
873 	if (parent) {
874 		hammer2_chain_unlock(parent);
875 		hammer2_chain_drop(parent);
876 	}
877 }
878 
879 /*
880  * Generic scan
881  *
882  * WARNING! Fed chains must be locked shared so ownership can be transfered
883  *	    and to prevent frontend/backend stalls that would occur with an
884  *	    exclusive lock.  The shared lock also allows chain->data to be
885  *	    retained.
886  */
887 void
888 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex)
889 {
890 	hammer2_xop_scanall_t *xop = &arg->xop_scanall;
891 	hammer2_chain_t *parent;
892 	hammer2_chain_t *chain;
893 	hammer2_key_t key_next;
894 	int cache_index = -1;
895 	int error = 0;
896 
897 	/*
898 	 * Assert required flags.
899 	 */
900 	KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED);
901 	KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED);
902 
903 	/*
904 	 * The inode's chain is the iterator.  If we cannot acquire it our
905 	 * contribution ends here.
906 	 */
907 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
908 				     xop->resolve_flags);
909 	if (parent == NULL) {
910 		kprintf("xop_readdir: NULL parent\n");
911 		goto done;
912 	}
913 
914 	/*
915 	 * Generic scan of exact records.  Note that indirect blocks are
916 	 * automatically recursed and will not be returned.
917 	 */
918 	chain = hammer2_chain_lookup(&parent, &key_next,
919 				     xop->key_beg, xop->key_end,
920 				     &cache_index, xop->lookup_flags);
921 	while (chain) {
922 		error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
923 		if (error)
924 			break;
925 		chain = hammer2_chain_next(&parent, chain, &key_next,
926 					   key_next, xop->key_end,
927 					   &cache_index, xop->lookup_flags);
928 	}
929 	if (chain) {
930 		hammer2_chain_unlock(chain);
931 		hammer2_chain_drop(chain);
932 	}
933 	hammer2_chain_unlock(parent);
934 	hammer2_chain_drop(parent);
935 done:
936 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
937 }
938