hammer2 - Stabilization pass
[dragonfly.git] / sys / vfs / hammer2 / hammer2_xops.c
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 }