xref: /dragonfly/sys/vfs/hammer2/hammer2_xops.c (revision 279dd846)
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 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.ip1, 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.name1;
264 	name_len = xop->head.name1_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,
320 					     xop->head.mtid, xop->dopermanent);
321 		}
322 	}
323 
324 	/*
325 	 * If the entry is a hardlink pointer, resolve it.  If this is the
326 	 * last link, delete it.  We aren't the frontend so we can't adjust
327 	 * nlinks.
328 	 */
329 	if (chain) {
330 		if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
331 			error = hammer2_chain_hardlink_find(
332 						xop->head.ip1,
333 						&parent, &chain,
334 						0);
335 			if (chain &&
336 			    (int64_t)chain->data->ipdata.meta.nlinks <= 1) {
337 				hammer2_chain_delete(parent, chain,
338 						     xop->head.mtid,
339 						     xop->dopermanent);
340 			}
341 		}
342 	}
343 
344 	/*
345 	 * Chains passed to feed are expected to be locked shared.
346 	 */
347 	if (chain) {
348 		hammer2_chain_unlock(chain);
349 		hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS |
350 					  HAMMER2_RESOLVE_SHARED);
351 	}
352 
353 	/*
354 	 * We always return the hardlink target (the real inode) for
355 	 * further action.
356 	 */
357 done:
358 	hammer2_xop_feed(&xop->head, chain, clindex, error);
359 	if (chain)
360 		hammer2_chain_drop(chain);
361 	if (parent) {
362 		hammer2_chain_unlock(parent);
363 		hammer2_chain_drop(parent);
364 	}
365 }
366 
367 /*
368  * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
369  *
370  * Convert the target {dip,ip} to a hardlink target and replace
371  * the original namespace with a hardlink pointer.
372  */
373 void
374 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex)
375 {
376 	hammer2_xop_nlink_t *xop = &arg->xop_nlink;
377 	hammer2_pfs_t *pmp;
378 	hammer2_inode_data_t *wipdata;
379 	hammer2_chain_t *parent;
380 	hammer2_chain_t *chain;
381 	hammer2_chain_t *tmp;
382 	hammer2_inode_t *ip;
383 	hammer2_key_t key_dummy;
384 	int cache_index = -1;
385 	int error;
386 
387 	/*
388 	 * We need the precise parent chain to issue the deletion.
389 	 */
390 	ip = xop->head.ip2;
391 	pmp = ip->pmp;
392 	parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
393 	if (parent)
394 		hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
395 	if (parent == NULL) {
396 		chain = NULL;
397 		error = EIO;
398 		goto done;
399 	}
400 	chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
401 	if (chain == NULL) {
402 		error = EIO;
403 		goto done;
404 	}
405 	hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
406 
407 	/*
408 	 * Replace the namespace with a hardlink pointer if the chain being
409 	 * moved is not already a hardlink target.
410 	 */
411 	if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
412 		tmp = NULL;
413 		error = hammer2_chain_create(&parent, &tmp, pmp,
414 					     chain->bref.key, 0,
415 					     HAMMER2_BREF_TYPE_INODE,
416 					     HAMMER2_INODE_BYTES,
417 					     xop->head.mtid, 0, 0);
418 		if (error)
419 			goto done;
420 		hammer2_chain_modify(tmp, xop->head.mtid, 0, 0);
421 		wipdata = &tmp->data->ipdata;
422 		bzero(wipdata, sizeof(*wipdata));
423 		wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
424 		wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
425 		bcopy(chain->data->ipdata.filename, wipdata->filename,
426 		      chain->data->ipdata.meta.name_len);
427 		wipdata->meta.target_type = chain->data->ipdata.meta.type;
428 		wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
429 		wipdata->meta.inum = ip->meta.inum;
430 		wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
431 		wipdata->meta.nlinks = 1;
432 		wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
433 
434 		hammer2_chain_unlock(tmp);
435 		hammer2_chain_drop(tmp);
436 	}
437 
438 	hammer2_chain_unlock(parent);
439 	hammer2_chain_drop(parent);
440 
441 	/*
442 	 * Ok, back to the deleted chain.  We must reconnect this chain
443 	 * as a hardlink target to cdir (ip3).
444 	 *
445 	 * WARNING! Frontend assumes filename length is 18 bytes.
446 	 */
447 	hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
448 	wipdata = &chain->data->ipdata;
449 	ksnprintf(wipdata->filename, sizeof(wipdata->filename),
450 		  "0x%016jx", (intmax_t)ip->meta.inum);
451 	wipdata->meta.name_len = strlen(wipdata->filename);
452 	wipdata->meta.name_key = ip->meta.inum;
453 
454 	/*
455 	 * We must seek parent properly for the create.
456 	 */
457 	parent = hammer2_inode_chain(xop->head.ip3, clindex,
458 				     HAMMER2_RESOLVE_ALWAYS);
459 	if (parent == NULL) {
460 		error = EIO;
461 		goto done;
462 	}
463 	tmp = hammer2_chain_lookup(&parent, &key_dummy,
464 				   ip->meta.inum, ip->meta.inum,
465 				   &cache_index, 0);
466 	if (tmp) {
467 		hammer2_chain_unlock(tmp);
468 		hammer2_chain_drop(tmp);
469 		error = EEXIST;
470 		goto done;
471 	}
472 	error = hammer2_chain_create(&parent, &chain, pmp,
473 				     wipdata->meta.name_key, 0,
474 				     HAMMER2_BREF_TYPE_INODE,
475 				     HAMMER2_INODE_BYTES,
476 				     xop->head.mtid, 0, 0);
477 	/*
478 	 * To avoid having to scan the collision space we can simply
479 	 * reuse the inode's original name_key.  But ip->meta.name_key
480 	 * may have already been updated by the front-end, so use xop->lhc.
481 	 *
482 	 * (frontend is responsible for fixing up ip->pip).
483 	 */
484 done:
485 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
486 	if (parent) {
487 		hammer2_chain_unlock(parent);
488 		hammer2_chain_drop(parent);
489 	}
490 	if (chain) {
491 		hammer2_chain_unlock(chain);
492 		hammer2_chain_drop(chain);
493 	}
494 }
495 
496 /*
497  * Backend for hammer2_vop_nrename()
498  *
499  * This handles the final step of renaming, either renaming the
500  * actual inode or renaming the hardlink pointer.
501  */
502 void
503 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
504 {
505 	hammer2_xop_nrename_t *xop = &arg->xop_nrename;
506 	hammer2_pfs_t *pmp;
507 	hammer2_chain_t *parent;
508 	hammer2_chain_t *chain;
509 	hammer2_chain_t *tmp;
510 	hammer2_inode_t *ip;
511 	hammer2_key_t key_dummy;
512 	int cache_index = -1;
513 	int error;
514 
515 	/*
516 	 * We need the precise parent chain to issue the deletion.
517 	 *
518 	 * If this is not a hardlink target we can act on the inode,
519 	 * otherwise we have to locate the hardlink pointer.
520 	 */
521 	ip = xop->head.ip2;
522 	pmp = ip->pmp;
523 	chain = NULL;
524 
525 	if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
526 		/*
527 		 * Find ip's direct parent chain.
528 		 */
529 		parent = hammer2_inode_chain(ip, clindex,
530 					     HAMMER2_RESOLVE_ALWAYS);
531 		if (parent)
532 			hammer2_chain_getparent(&parent,
533 						HAMMER2_RESOLVE_ALWAYS);
534 		if (parent == NULL) {
535 			error = EIO;
536 			goto done;
537 		}
538 		chain = hammer2_inode_chain(ip, clindex,
539 					    HAMMER2_RESOLVE_ALWAYS);
540 		if (chain == NULL) {
541 			error = EIO;
542 			goto done;
543 		}
544 	} else {
545 		/*
546 		 * head.ip1 is fdip, do a namespace search.
547 		 */
548 		const hammer2_inode_data_t *ripdata;
549 		hammer2_key_t lhc;
550 		hammer2_key_t key_next;
551 		const char *name;
552 		size_t name_len;
553 
554 		parent = hammer2_inode_chain(xop->head.ip1, clindex,
555 					     HAMMER2_RESOLVE_ALWAYS |
556 					     HAMMER2_RESOLVE_SHARED);
557 		if (parent == NULL) {
558 			kprintf("xop_nrename: NULL parent\n");
559 			error = EIO;
560 			goto done;
561 		}
562 		name = xop->head.name1;
563 		name_len = xop->head.name1_len;
564 
565 		/*
566 		 * Lookup the directory entry
567 		 */
568 		lhc = hammer2_dirhash(name, name_len);
569 		chain = hammer2_chain_lookup(&parent, &key_next,
570 					     lhc, lhc + HAMMER2_DIRHASH_LOMASK,
571 					     &cache_index,
572 					     HAMMER2_LOOKUP_ALWAYS);
573 		while (chain) {
574 			ripdata = &chain->data->ipdata;
575 			if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
576 			    ripdata->meta.name_len == name_len &&
577 			    bcmp(ripdata->filename, name, name_len) == 0) {
578 				break;
579 			}
580 			chain = hammer2_chain_next(&parent, chain, &key_next,
581 						   key_next,
582 						   lhc + HAMMER2_DIRHASH_LOMASK,
583 						   &cache_index,
584 						   HAMMER2_LOOKUP_ALWAYS);
585 		}
586 	}
587 
588 	/*
589 	 * Delete it, then create it in the new namespace.
590 	 */
591 	hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
592 	hammer2_chain_unlock(parent);
593 	hammer2_chain_drop(parent);
594 	parent = NULL;		/* safety */
595 
596 
597 	/*
598 	 * Ok, back to the deleted chain.  We must reconnect this chain
599 	 * to tdir (ip3).  The chain (a real inode or a hardlink pointer)
600 	 * is not otherwise modified.
601 	 *
602 	 * Frontend is expected to replicate the same inode meta data
603 	 * modifications.
604 	 *
605 	 * NOTE!  This chain may not represent the actual inode, it
606 	 *	  can be a hardlink pointer.
607 	 *
608 	 * XXX in-inode parent directory specification?
609 	 */
610 	if (chain->data->ipdata.meta.name_key != xop->lhc ||
611 	    xop->head.name1_len != xop->head.name2_len ||
612 	    bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
613 		hammer2_inode_data_t *wipdata;
614 
615 		hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
616 		wipdata = &chain->data->ipdata;
617 
618 		bzero(wipdata->filename, sizeof(wipdata->filename));
619 		bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
620 		wipdata->meta.name_key = xop->lhc;
621 		wipdata->meta.name_len = xop->head.name2_len;
622 	}
623 
624 	/*
625 	 * We must seek parent properly for the create.
626 	 */
627 	parent = hammer2_inode_chain(xop->head.ip3, clindex,
628 				     HAMMER2_RESOLVE_ALWAYS);
629 	if (parent == NULL) {
630 		error = EIO;
631 		goto done;
632 	}
633 	tmp = hammer2_chain_lookup(&parent, &key_dummy,
634 				   xop->lhc, xop->lhc,
635 				   &cache_index, 0);
636 	if (tmp) {
637 		hammer2_chain_unlock(tmp);
638 		hammer2_chain_drop(tmp);
639 		error = EEXIST;
640 		goto done;
641 	}
642 
643 	error = hammer2_chain_create(&parent, &chain, pmp,
644 				     xop->lhc, 0,
645 				     HAMMER2_BREF_TYPE_INODE,
646 				     HAMMER2_INODE_BYTES,
647 				     xop->head.mtid, 0, 0);
648 	/*
649 	 * (frontend is responsible for fixing up ip->pip).
650 	 */
651 done:
652 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
653 	if (parent) {
654 		hammer2_chain_unlock(parent);
655 		hammer2_chain_drop(parent);
656 	}
657 	if (chain) {
658 		hammer2_chain_unlock(chain);
659 		hammer2_chain_drop(chain);
660 	}
661 }
662 
663 /*
664  * Directory collision resolver scan helper (backend, threaded).
665  *
666  * Used by the inode create code to locate an unused lhc.
667  */
668 void
669 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex)
670 {
671 	hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
672 	hammer2_chain_t *parent;
673 	hammer2_chain_t *chain;
674 	hammer2_key_t key_next;
675 	int cache_index = -1;	/* XXX */
676 	int error = 0;
677 
678 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
679 				     HAMMER2_RESOLVE_ALWAYS |
680 				     HAMMER2_RESOLVE_SHARED);
681 	if (parent == NULL) {
682 		kprintf("xop_nresolve: NULL parent\n");
683 		chain = NULL;
684 		error = EIO;
685 		goto done;
686 	}
687 
688 	/*
689 	 * Lookup all possibly conflicting directory entries, the feed
690 	 * inherits the chain's lock so do not unlock it on the iteration.
691 	 */
692 	chain = hammer2_chain_lookup(&parent, &key_next,
693 				     xop->lhc,
694 				     xop->lhc + HAMMER2_DIRHASH_LOMASK,
695 				     &cache_index,
696 				     HAMMER2_LOOKUP_ALWAYS |
697 				     HAMMER2_LOOKUP_SHARED);
698 	while (chain) {
699 		error = hammer2_xop_feed(&xop->head, chain, clindex,
700 					 chain->error);
701 		if (error) {
702 			hammer2_chain_drop(chain);
703 			chain = NULL;	/* safety */
704 			break;
705 		}
706 		chain = hammer2_chain_next(&parent, chain, &key_next,
707 					   key_next,
708 					   xop->lhc + HAMMER2_DIRHASH_LOMASK,
709 					   &cache_index,
710 					   HAMMER2_LOOKUP_ALWAYS |
711 					   HAMMER2_LOOKUP_SHARED |
712 					   HAMMER2_LOOKUP_NOUNLOCK);
713 	}
714 done:
715 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
716 	if (parent) {
717 		hammer2_chain_unlock(parent);
718 		hammer2_chain_drop(parent);
719 	}
720 }
721 
722 /*
723  * Generic lookup of a specific key.
724  *
725  * Used by the inode hidden directory code to find the hidden directory.
726  */
727 void
728 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex)
729 {
730 	hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
731 	hammer2_chain_t *parent;
732 	hammer2_chain_t *chain;
733 	hammer2_key_t key_next;
734 	int cache_index = -1;	/* XXX */
735 	int error = 0;
736 
737 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
738 				     HAMMER2_RESOLVE_ALWAYS |
739 				     HAMMER2_RESOLVE_SHARED);
740 	chain = NULL;
741 	if (parent == NULL) {
742 		error = EIO;
743 		goto done;
744 	}
745 
746 	/*
747 	 * Lookup all possibly conflicting directory entries, the feed
748 	 * inherits the chain's lock so do not unlock it on the iteration.
749 	 */
750 	chain = hammer2_chain_lookup(&parent, &key_next,
751 				     xop->lhc, xop->lhc,
752 				     &cache_index,
753 				     HAMMER2_LOOKUP_ALWAYS |
754 				     HAMMER2_LOOKUP_SHARED);
755 	if (chain)
756 		hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
757 	else
758 		hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT);
759 
760 done:
761 	if (chain) {
762 		/* leave lock intact for feed */
763 		hammer2_chain_drop(chain);
764 	}
765 	if (parent) {
766 		hammer2_chain_unlock(parent);
767 		hammer2_chain_drop(parent);
768 	}
769 }
770 
771 /*
772  * Generic scan
773  */
774 void
775 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex)
776 {
777 	hammer2_xop_scanall_t *xop = &arg->xop_scanall;
778 	hammer2_chain_t *parent;
779 	hammer2_chain_t *chain;
780 	hammer2_key_t key_next;
781 	int cache_index = -1;
782 	int error = 0;
783 
784 	/*
785 	 * The inode's chain is the iterator.  If we cannot acquire it our
786 	 * contribution ends here.
787 	 */
788 	parent = hammer2_inode_chain(xop->head.ip1, clindex,
789 				     HAMMER2_RESOLVE_ALWAYS |
790 				     HAMMER2_RESOLVE_SHARED);
791 	if (parent == NULL) {
792 		kprintf("xop_readdir: NULL parent\n");
793 		goto done;
794 	}
795 
796 	/*
797 	 * Generic scan of exact records.  Note that indirect blocks are
798 	 * automatically recursed and will not be returned.
799 	 */
800 	chain = hammer2_chain_lookup(&parent, &key_next,
801 				     xop->key_beg, xop->key_end,
802 				     &cache_index, HAMMER2_LOOKUP_SHARED |
803 						   HAMMER2_LOOKUP_NODIRECT);
804 	while (chain) {
805 		error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
806 		if (error)
807 			break;
808 		chain = hammer2_chain_next(&parent, chain, &key_next,
809 					   key_next, xop->key_end,
810 					   &cache_index,
811 					   HAMMER2_LOOKUP_SHARED |
812 					   HAMMER2_LOOKUP_NODIRECT |
813 					   HAMMER2_LOOKUP_NOUNLOCK);
814 	}
815 	if (chain)
816 		hammer2_chain_drop(chain);
817 	hammer2_chain_unlock(parent);
818 	hammer2_chain_drop(parent);
819 done:
820 	hammer2_xop_feed(&xop->head, NULL, clindex, error);
821 }
822