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