xref: /dflybsd-src/sys/vfs/hammer2/hammer2_xops.c (revision d63676ccce44debffd22823892f437449f6acdad)
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_vfsroot(hammer2_xop_t *arg, int clindex)
71 {
72 	hammer2_xop_vfsroot_t *xop = &arg->xop_vfsroot;
73 	hammer2_chain_t *chain;
74 	int error;
75 
76 	chain = hammer2_inode_chain(xop->head.ip, 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->head.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.ip, 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_t)-1,
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_t)-1,
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.ip, 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.name;
176 	name_len = xop->head.name_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.ip,
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 does 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 	uint8_t type;
247 	hammer2_key_t key_next;
248 	hammer2_key_t lhc;
249 	int cache_index = -1;	/* XXX */
250 	int error;
251 
252 	/*
253 	 * Requires exclusive lock
254 	 */
255 	parent = hammer2_inode_chain(xop->head.ip, clindex,
256 				     HAMMER2_RESOLVE_ALWAYS);
257 	if (parent == NULL) {
258 		kprintf("xop_nresolve: NULL parent\n");
259 		chain = NULL;
260 		error = EIO;
261 		goto done;
262 	}
263 	name = xop->head.name;
264 	name_len = xop->head.name_len;
265 
266 	/*
267 	 * Lookup the directory entry
268 	 */
269 	lhc = hammer2_dirhash(name, name_len);
270 	chain = hammer2_chain_lookup(&parent, &key_next,
271 				     lhc, lhc + HAMMER2_DIRHASH_LOMASK,
272 				     &cache_index,
273 				     HAMMER2_LOOKUP_ALWAYS);
274 	while (chain) {
275 		ripdata = &chain->data->ipdata;
276 		if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
277 		    ripdata->meta.name_len == name_len &&
278 		    bcmp(ripdata->filename, name, name_len) == 0) {
279 			break;
280 		}
281 		chain = hammer2_chain_next(&parent, chain, &key_next,
282 					   key_next,
283 					   lhc + HAMMER2_DIRHASH_LOMASK,
284 					   &cache_index,
285 					   HAMMER2_LOOKUP_ALWAYS);
286 	}
287 
288 	/*
289 	 * If the directory entry is a HARDLINK pointer then obtain the
290 	 * underlying file type for the directory typing tests and delete
291 	 * the HARDLINK pointer chain permanently.  The frontend is left
292 	 * responsible for handling nlinks on and deleting the actual inode.
293 	 *
294 	 * If the directory entry is the actual inode then use its type
295 	 * for the directory typing tests and delete the chain, permanency
296 	 * depends on whether the inode is open or not.
297 	 *
298 	 * Check directory typing and delete the entry.  Note that
299 	 * nlinks adjustments are made on the real inode by the frontend,
300 	 * not here.
301 	 */
302 	error = 0;
303 	if (chain) {
304 		int dopermanent = xop->dopermanent;
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 		if (type == HAMMER2_OBJTYPE_DIRECTORY &&
312 		    xop->isdir == 0) {
313 			error = ENOTDIR;
314 		} else
315 		if (type != HAMMER2_OBJTYPE_DIRECTORY &&
316 		    xop->isdir >= 1) {
317 			error = EISDIR;
318 		} else {
319 			hammer2_chain_delete(parent, chain, xop->dopermanent);
320 		}
321 	}
322 
323 	/*
324 	 * If the entry is a hardlink pointer, resolve it.  If this is the
325 	 * last link, delete it.  We aren't the frontend so we can't adjust
326 	 * nlinks.
327 	 */
328 	if (chain) {
329 		if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
330 			error = hammer2_chain_hardlink_find(
331 						xop->head.ip,
332 						&parent, &chain,
333 						0);
334 			if (chain &&
335 			    (int64_t)chain->data->ipdata.meta.nlinks <= 1) {
336 				hammer2_chain_delete(parent, chain,
337 						     xop->dopermanent);
338 			}
339 		}
340 	}
341 
342 	/*
343 	 * Chains passed to feed are expected to be locked shared.
344 	 */
345 	if (chain) {
346 		hammer2_chain_unlock(chain);
347 		hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS |
348 					  HAMMER2_RESOLVE_SHARED);
349 	}
350 
351 	/*
352 	 * We always return the hardlink target (the real inode) for
353 	 * further action.
354 	 */
355 done:
356 	hammer2_xop_feed(&xop->head, chain, clindex, error);
357 	if (chain)
358 		hammer2_chain_drop(chain);
359 	if (parent) {
360 		hammer2_chain_unlock(parent);
361 		hammer2_chain_drop(parent);
362 	}
363 }
364 
365 /*
366  * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
367  *
368  * Convert the target {dip,ip} to a hardlink target and replace
369  * the original namespace with a hardlink pointer.
370  */
371 void
372 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex)
373 {
374 	hammer2_xop_nlink_t *xop = &arg->xop_nlink;
375 	hammer2_pfs_t *pmp;
376 	hammer2_inode_data_t *wipdata;
377 	hammer2_chain_t *parent;
378 	hammer2_chain_t *chain;
379 	hammer2_chain_t *tmp;
380 	hammer2_inode_t *ip;
381 	hammer2_key_t key_dummy;
382 	int cache_index = -1;
383 	int error;
384 
385 	/*
386 	 * We need the precise parent chain to issue the deletion.
387 	 */
388 	ip = xop->head.ip2;
389 	pmp = ip->pmp;
390 	parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
391 	if (parent)
392 		hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
393 	if (parent == NULL) {
394 		chain = NULL;
395 		error = EIO;
396 		goto done;
397 	}
398 	chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
399 	if (chain == NULL) {
400 		error = EIO;
401 		goto done;
402 	}
403 	hammer2_chain_delete(parent, chain, 0);
404 
405 	/*
406 	 * Replace the namespace with a hardlink pointer if the chain being
407 	 * moved is not already a hardlink target.
408 	 */
409 	if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
410 		tmp = NULL;
411 		error = hammer2_chain_create(&parent, &tmp, pmp,
412 					     chain->bref.key, 0,
413 					     HAMMER2_BREF_TYPE_INODE,
414 					     HAMMER2_INODE_BYTES,
415 					     0);
416 		if (error)
417 			goto done;
418 		hammer2_chain_modify(tmp, 0);
419 		wipdata = &tmp->data->ipdata;
420 		bzero(wipdata, sizeof(*wipdata));
421 		wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
422 		wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
423 		bcopy(chain->data->ipdata.filename, wipdata->filename,
424 		      chain->data->ipdata.meta.name_len);
425 		wipdata->meta.target_type = chain->data->ipdata.meta.type;
426 		wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
427 		wipdata->meta.inum = ip->meta.inum;
428 		wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
429 		wipdata->meta.nlinks = 1;
430 		wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
431 
432 		hammer2_chain_unlock(tmp);
433 		hammer2_chain_drop(tmp);
434 	}
435 
436 	hammer2_chain_unlock(parent);
437 	hammer2_chain_drop(parent);
438 
439 	/*
440 	 * Ok, back to the deleted chain.  We must reconnect this chain
441 	 * as a hardlink target to cdir (ip3).
442 	 *
443 	 * WARNING! Frontend assumes filename length is 18 bytes.
444 	 */
445 	hammer2_chain_modify(chain, 0);
446 	wipdata = &chain->data->ipdata;
447 	ksnprintf(wipdata->filename, sizeof(wipdata->filename),
448 		  "0x%016jx", (intmax_t)ip->meta.inum);
449 	wipdata->meta.name_len = strlen(wipdata->filename);
450 	wipdata->meta.name_key = ip->meta.inum;
451 
452 	/*
453 	 * We must seek parent properly for the create.
454 	 */
455 	parent = hammer2_inode_chain(xop->head.ip3, clindex,
456 				     HAMMER2_RESOLVE_ALWAYS);
457 	if (parent == NULL) {
458 		error = EIO;
459 		goto done;
460 	}
461 	tmp = hammer2_chain_lookup(&parent, &key_dummy,
462 				   ip->meta.inum, ip->meta.inum,
463 				   &cache_index, 0);
464 	if (tmp) {
465 		hammer2_chain_unlock(tmp);
466 		hammer2_chain_drop(tmp);
467 		error = EEXIST;
468 		goto done;
469 	}
470 	error = hammer2_chain_create(&parent, &chain, pmp,
471 				     wipdata->meta.name_key, 0,
472 				     HAMMER2_BREF_TYPE_INODE,
473 				     HAMMER2_INODE_BYTES,
474 				     0);
475 	/*
476 	 * To avoid having to scan the collision space we can simply
477 	 * reuse the inode's original name_key.  But ip->meta.name_key
478 	 * may have already been updated by the front-end, so use xop->lhc.
479 	 *
480 	 * (frontend is responsible for fixing up ip->pip).
481 	 */
482 done:
483 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
484 	if (parent) {
485 		hammer2_chain_unlock(parent);
486 		hammer2_chain_drop(parent);
487 	}
488 	if (chain) {
489 		hammer2_chain_unlock(chain);
490 		hammer2_chain_drop(chain);
491 	}
492 }
493 
494 /*
495  * Backend for hammer2_vop_nrename()
496  *
497  * This handles the final step of renaming, either renaming the
498  * actual inode or renaming the hardlink pointer.
499  */
500 void
501 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
502 {
503 	hammer2_xop_nrename_t *xop = &arg->xop_nrename;
504 	hammer2_pfs_t *pmp;
505 	hammer2_chain_t *parent;
506 	hammer2_chain_t *chain;
507 	hammer2_chain_t *tmp;
508 	hammer2_inode_t *ip;
509 	hammer2_key_t key_dummy;
510 	int cache_index = -1;
511 	int error;
512 
513 	/*
514 	 * We need the precise parent chain to issue the deletion.
515 	 *
516 	 * If this is not a hardlink target we can act on the inode,
517 	 * otherwise we have to locate the hardlink pointer.
518 	 */
519 	ip = xop->head.ip2;
520 	pmp = ip->pmp;
521 	chain = NULL;
522 
523 	if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
524 		/*
525 		 * Find ip's direct parent chain.
526 		 */
527 		parent = hammer2_inode_chain(ip, clindex,
528 					     HAMMER2_RESOLVE_ALWAYS);
529 		if (parent)
530 			hammer2_chain_getparent(&parent,
531 						HAMMER2_RESOLVE_ALWAYS);
532 		if (parent == NULL) {
533 			error = EIO;
534 			goto done;
535 		}
536 		chain = hammer2_inode_chain(ip, clindex,
537 					    HAMMER2_RESOLVE_ALWAYS);
538 		if (chain == NULL) {
539 			error = EIO;
540 			goto done;
541 		}
542 	} else {
543 		/*
544 		 * head.ip is fdip, do a namespace search.
545 		 */
546 		const hammer2_inode_data_t *ripdata;
547 		hammer2_key_t lhc;
548 		hammer2_key_t key_next;
549 		const char *name;
550 		size_t name_len;
551 
552 		parent = hammer2_inode_chain(xop->head.ip, clindex,
553 					     HAMMER2_RESOLVE_ALWAYS |
554 					     HAMMER2_RESOLVE_SHARED);
555 		if (parent == NULL) {
556 			kprintf("xop_nrename: NULL parent\n");
557 			error = EIO;
558 			goto done;
559 		}
560 		name = xop->head.name;
561 		name_len = xop->head.name_len;
562 
563 		/*
564 		 * Lookup the directory entry
565 		 */
566 		lhc = hammer2_dirhash(name, name_len);
567 		chain = hammer2_chain_lookup(&parent, &key_next,
568 					     lhc, lhc + HAMMER2_DIRHASH_LOMASK,
569 					     &cache_index,
570 					     HAMMER2_LOOKUP_ALWAYS);
571 		while (chain) {
572 			ripdata = &chain->data->ipdata;
573 			if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
574 			    ripdata->meta.name_len == name_len &&
575 			    bcmp(ripdata->filename, name, name_len) == 0) {
576 				break;
577 			}
578 			chain = hammer2_chain_next(&parent, chain, &key_next,
579 						   key_next,
580 						   lhc + HAMMER2_DIRHASH_LOMASK,
581 						   &cache_index,
582 						   HAMMER2_LOOKUP_ALWAYS);
583 		}
584 	}
585 
586 	/*
587 	 * Delete it, then create it in the new namespace.
588 	 */
589 	hammer2_chain_delete(parent, chain, 0);
590 	hammer2_chain_unlock(parent);
591 	hammer2_chain_drop(parent);
592 	parent = NULL;		/* safety */
593 
594 
595 	/*
596 	 * Ok, back to the deleted chain.  We must reconnect this chain
597 	 * to tdir (ip3).  The chain (a real inode or a hardlink pointer)
598 	 * is not otherwise modified.
599 	 *
600 	 * Frontend is expected to replicate the same inode meta data
601 	 * modifications.
602 	 *
603 	 * NOTE!  This chain may not represent the actual inode, it
604 	 *	  can be a hardlink pointer.
605 	 *
606 	 * XXX in-inode parent directory specification?
607 	 */
608 	if (chain->data->ipdata.meta.name_key != xop->lhc ||
609 	    xop->head.name_len != xop->head.name2_len ||
610 	    bcmp(xop->head.name, xop->head.name2, xop->head.name_len) != 0) {
611 		hammer2_inode_data_t *wipdata;
612 
613 		hammer2_chain_modify(chain, 0);
614 		wipdata = &chain->data->ipdata;
615 
616 		bzero(wipdata->filename, sizeof(wipdata->filename));
617 		bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
618 		wipdata->meta.name_key = xop->lhc;
619 		wipdata->meta.name_len = xop->head.name2_len;
620 	}
621 
622 	/*
623 	 * We must seek parent properly for the create.
624 	 */
625 	parent = hammer2_inode_chain(xop->head.ip3, clindex,
626 				     HAMMER2_RESOLVE_ALWAYS);
627 	if (parent == NULL) {
628 		error = EIO;
629 		goto done;
630 	}
631 	tmp = hammer2_chain_lookup(&parent, &key_dummy,
632 				   xop->lhc, xop->lhc,
633 				   &cache_index, 0);
634 	if (tmp) {
635 		hammer2_chain_unlock(tmp);
636 		hammer2_chain_drop(tmp);
637 		error = EEXIST;
638 		goto done;
639 	}
640 
641 	error = hammer2_chain_create(&parent, &chain, pmp,
642 				     xop->lhc, 0,
643 				     HAMMER2_BREF_TYPE_INODE,
644 				     HAMMER2_INODE_BYTES,
645 				     0);
646 	/*
647 	 * (frontend is responsible for fixing up ip->pip).
648 	 */
649 done:
650 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
651 	if (parent) {
652 		hammer2_chain_unlock(parent);
653 		hammer2_chain_drop(parent);
654 	}
655 	if (chain) {
656 		hammer2_chain_unlock(chain);
657 		hammer2_chain_drop(chain);
658 	}
659 }
660 
661 /*
662  * Directory collision resolver scan helper (backend, threaded).
663  *
664  * Used by the inode create code to locate an unused lhc.
665  */
666 void
667 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex)
668 {
669 	hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
670 	hammer2_chain_t *parent;
671 	hammer2_chain_t *chain;
672 	hammer2_key_t key_next;
673 	int cache_index = -1;	/* XXX */
674 	int error = 0;
675 
676 	parent = hammer2_inode_chain(xop->head.ip, clindex,
677 				     HAMMER2_RESOLVE_ALWAYS |
678 				     HAMMER2_RESOLVE_SHARED);
679 	if (parent == NULL) {
680 		kprintf("xop_nresolve: NULL parent\n");
681 		chain = NULL;
682 		error = EIO;
683 		goto done;
684 	}
685 
686 	/*
687 	 * Lookup all possibly conflicting directory entries, the feed
688 	 * inherits the chain's lock so do not unlock it on the iteration.
689 	 */
690 	chain = hammer2_chain_lookup(&parent, &key_next,
691 				     xop->lhc,
692 				     xop->lhc + HAMMER2_DIRHASH_LOMASK,
693 				     &cache_index,
694 				     HAMMER2_LOOKUP_ALWAYS |
695 				     HAMMER2_LOOKUP_SHARED);
696 	while (chain) {
697 		error = hammer2_xop_feed(&xop->head, chain, clindex,
698 					 chain->error);
699 		if (error) {
700 			hammer2_chain_drop(chain);
701 			chain = NULL;	/* safety */
702 			break;
703 		}
704 		chain = hammer2_chain_next(&parent, chain, &key_next,
705 					   key_next,
706 					   xop->lhc + HAMMER2_DIRHASH_LOMASK,
707 					   &cache_index,
708 					   HAMMER2_LOOKUP_ALWAYS |
709 					   HAMMER2_LOOKUP_SHARED |
710 					   HAMMER2_LOOKUP_NOUNLOCK);
711 	}
712 done:
713 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
714 	if (parent) {
715 		hammer2_chain_unlock(parent);
716 		hammer2_chain_drop(parent);
717 	}
718 }
719 
720 /*
721  * Lookup a specific key.
722  *
723  * Used by the inode hidden directory code to find the hidden directory.
724  */
725 void
726 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex)
727 {
728 	hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
729 	hammer2_chain_t *parent;
730 	hammer2_chain_t *chain;
731 	hammer2_key_t key_next;
732 	int cache_index = -1;	/* XXX */
733 	int error = 0;
734 
735 	parent = hammer2_inode_chain(xop->head.ip, clindex,
736 				     HAMMER2_RESOLVE_ALWAYS |
737 				     HAMMER2_RESOLVE_SHARED);
738 	chain = NULL;
739 	if (parent == NULL) {
740 		error = EIO;
741 		goto done;
742 	}
743 
744 	/*
745 	 * Lookup all possibly conflicting directory entries, the feed
746 	 * inherits the chain's lock so do not unlock it on the iteration.
747 	 */
748 	chain = hammer2_chain_lookup(&parent, &key_next,
749 				     xop->lhc, xop->lhc,
750 				     &cache_index,
751 				     HAMMER2_LOOKUP_ALWAYS |
752 				     HAMMER2_LOOKUP_SHARED);
753 	if (chain)
754 		hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
755 	else
756 		hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT);
757 
758 done:
759 	if (chain) {
760 		/* leave lock intact for feed */
761 		hammer2_chain_drop(chain);
762 	}
763 	if (parent) {
764 		hammer2_chain_unlock(parent);
765 		hammer2_chain_drop(parent);
766 	}
767 }
768