hammer2 - stabilization - Fix a number of bugs revealed by fsx and fsstress.
[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  * 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 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         hammer2_key_t key_next;
247         hammer2_key_t lhc;
248         int cache_index = -1;   /* XXX */
249         int error;
250
251         /*
252          * Requires exclusive lock
253          */
254         parent = hammer2_inode_chain(xop->head.ip1, clindex,
255                                      HAMMER2_RESOLVE_ALWAYS);
256         if (parent == NULL) {
257                 kprintf("xop_nresolve: NULL parent\n");
258                 chain = NULL;
259                 error = EIO;
260                 goto done;
261         }
262         name = xop->head.name1;
263         name_len = xop->head.name1_len;
264
265         /*
266          * Lookup the directory entry
267          */
268         lhc = hammer2_dirhash(name, name_len);
269         chain = hammer2_chain_lookup(&parent, &key_next,
270                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
271                                      &cache_index,
272                                      HAMMER2_LOOKUP_ALWAYS);
273         while (chain) {
274                 ripdata = &chain->data->ipdata;
275                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
276                     ripdata->meta.name_len == name_len &&
277                     bcmp(ripdata->filename, name, name_len) == 0) {
278                         break;
279                 }
280                 chain = hammer2_chain_next(&parent, chain, &key_next,
281                                            key_next,
282                                            lhc + HAMMER2_DIRHASH_LOMASK,
283                                            &cache_index,
284                                            HAMMER2_LOOKUP_ALWAYS);
285         }
286
287         /*
288          * If the directory entry is a HARDLINK pointer then obtain the
289          * underlying file type for the directory typing tests and delete
290          * the HARDLINK pointer chain permanently.  The frontend is left
291          * responsible for handling nlinks on and deleting the actual inode.
292          *
293          * If the directory entry is the actual inode then use its type
294          * for the directory typing tests and delete the chain, permanency
295          * depends on whether the inode is open or not.
296          *
297          * Check directory typing and delete the entry.  Note that
298          * nlinks adjustments are made on the real inode by the frontend,
299          * not here.
300          */
301         error = 0;
302         if (chain) {
303                 int dopermanent = xop->dopermanent;
304                 uint8_t type;
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
312                 if (type == HAMMER2_OBJTYPE_DIRECTORY &&
313                     xop->isdir == 0) {
314                         error = ENOTDIR;
315                 } else 
316                 if (type != HAMMER2_OBJTYPE_DIRECTORY &&
317                     xop->isdir >= 1) {
318                         error = EISDIR;
319                 } else {
320                         /*
321                          * This deletes the directory entry itself, which is
322                          * also the inode when nlinks == 1.  Hardlink targets
323                          * are handled in the next conditional.
324                          */
325                         hammer2_chain_delete(parent, chain,
326                                              xop->head.mtid, dopermanent);
327                 }
328         }
329
330         /*
331          * If the entry is a hardlink pointer, resolve it.  If this is the
332          * last link, delete it.  The frontend has the master copy of nlinks
333          * but we still have to make adjustments here to synchronize with it.
334          *
335          * On delete / adjust nlinks if there is no error.  But we still need
336          * to resolve the hardlink to feed the inode's real chain back to
337          * the frontend.
338          *
339          * XXX we are basically tracking the nlinks count by doing a delta
340          *     adjustment instead of having the frontend pass the absolute
341          *     value down.  We really need to have the frontend pass the
342          *     absolute value down (difficult because there might not be
343          *     an 'ip').  See also hammer2_xop_nlink().
344          */
345         if (chain &&
346             chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
347                 int error2;
348
349                 error2 = hammer2_chain_hardlink_find(xop->head.ip1,
350                                                      &parent, &chain, 0);
351                 if (chain && error == 0 && error2 == 0 &&
352                     (int64_t)chain->data->ipdata.meta.nlinks <= 1) {
353                         hammer2_chain_delete(parent, chain,
354                                              xop->head.mtid,
355                                              xop->dopermanent);
356                 } else if (chain && error == 0 && error2 == 0) {
357                         hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
358                         --chain->data->ipdata.meta.nlinks;
359                 }
360                 if (error == 0)
361                         error = error2;
362         }
363
364         /*
365          * Chains passed to feed are expected to be locked shared.
366          */
367         if (chain) {
368                 hammer2_chain_unlock(chain);
369                 hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS |
370                                           HAMMER2_RESOLVE_SHARED);
371         }
372
373         /*
374          * We always return the hardlink target (the real inode) for
375          * further action.
376          */
377 done:
378         hammer2_xop_feed(&xop->head, chain, clindex, error);
379         if (chain)
380                 hammer2_chain_drop(chain);
381         if (parent) {
382                 hammer2_chain_unlock(parent);
383                 hammer2_chain_drop(parent);
384         }
385 }
386
387 /*
388  * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
389  *
390  * ip1 - fdip
391  * ip2 - ip
392  * ip3 - cdip
393  *
394  * If a hardlink pointer:
395  *      The existing hardlink target {fdip,ip} must be moved to another
396  *      directory {cdip,ip}
397  *
398  * If not a hardlink pointer:
399  *      Convert the target {fdip,ip} to a hardlink target {cdip,ip} and
400  *      replace the original namespace {fdip,name} with a hardlink pointer.
401  */
402 void
403 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex)
404 {
405         hammer2_xop_nlink_t *xop = &arg->xop_nlink;
406         hammer2_pfs_t *pmp;
407         hammer2_inode_data_t *wipdata;
408         hammer2_chain_t *parent;
409         hammer2_chain_t *chain;
410         hammer2_chain_t *tmp;
411         hammer2_inode_t *ip;
412         hammer2_key_t key_dummy;
413         int cache_index = -1;
414         int error;
415         int did_delete = 0;
416
417         /*
418          * We need the precise parent chain to issue the deletion.
419          */
420         ip = xop->head.ip2;
421         pmp = ip->pmp;
422         parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
423         if (parent)
424                 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
425         if (parent == NULL) {
426                 chain = NULL;
427                 error = EIO;
428                 goto done;
429         }
430         chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
431         if (chain == NULL) {
432                 error = EIO;
433                 goto done;
434         }
435         KKASSERT(chain->parent == parent);
436
437         if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
438                 /*
439                  * Delete the original chain and hold onto it for the move
440                  * to cdir.
441                  *
442                  * Replace the namespace with a hardlink pointer if the
443                  * chain being moved is not already a hardlink target.
444                  */
445                 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
446                 did_delete = 1;
447
448                 tmp = NULL;
449                 error = hammer2_chain_create(&parent, &tmp, pmp,
450                                              chain->bref.key, 0,
451                                              HAMMER2_BREF_TYPE_INODE,
452                                              HAMMER2_INODE_BYTES,
453                                              xop->head.mtid, 0, 0);
454                 if (error)
455                         goto done;
456                 hammer2_chain_modify(tmp, xop->head.mtid, 0, 0);
457                 wipdata = &tmp->data->ipdata;
458                 bzero(wipdata, sizeof(*wipdata));
459                 wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
460                 wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
461                 bcopy(chain->data->ipdata.filename, wipdata->filename,
462                       chain->data->ipdata.meta.name_len);
463                 wipdata->meta.target_type = chain->data->ipdata.meta.type;
464                 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
465                 wipdata->meta.inum = ip->meta.inum;
466                 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
467                 wipdata->meta.nlinks = 1;
468                 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
469
470                 hammer2_chain_unlock(tmp);
471                 hammer2_chain_drop(tmp);
472         } else if (xop->head.ip1 != xop->head.ip3) {
473                 /*
474                  * Delete the hardlink target so it can be moved
475                  * to cdir.
476                  */
477                 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
478                 did_delete = 1;
479         } else {
480                 /*
481                  * Deletion not necessary (just a nlinks update).
482                  */
483                 did_delete = 0;
484         }
485
486         hammer2_chain_unlock(parent);
487         hammer2_chain_drop(parent);
488         parent = NULL;
489
490         /*
491          * Ok, back to the deleted chain.  We must reconnect this chain
492          * as a hardlink target to cdir (ip3).
493          *
494          * WARNING! Frontend assumes filename length is 18 bytes.
495          */
496         if (did_delete) {
497                 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
498                 wipdata = &chain->data->ipdata;
499                 ksnprintf(wipdata->filename, sizeof(wipdata->filename),
500                           "0x%016jx", (intmax_t)ip->meta.inum);
501                 wipdata->meta.name_len = strlen(wipdata->filename);
502                 wipdata->meta.name_key = ip->meta.inum;
503
504                 /*
505                  * We must seek parent properly for the create to reattach
506                  * chain.  XXX just use chain->parent or
507                  * inode_chain_and_parent() ?
508                  */
509                 parent = hammer2_inode_chain(xop->head.ip3, clindex,
510                                              HAMMER2_RESOLVE_ALWAYS);
511                 if (parent == NULL) {
512                         error = EIO;
513                         goto done;
514                 }
515                 tmp = hammer2_chain_lookup(&parent, &key_dummy,
516                                            ip->meta.inum, ip->meta.inum,
517                                            &cache_index, 0);
518                 if (tmp) {
519                         hammer2_chain_unlock(tmp);
520                         hammer2_chain_drop(tmp);
521                         error = EEXIST;
522                         goto done;
523                 }
524                 error = hammer2_chain_create(&parent, &chain, pmp,
525                                              wipdata->meta.name_key, 0,
526                                              HAMMER2_BREF_TYPE_INODE,
527                                              HAMMER2_INODE_BYTES,
528                                              xop->head.mtid, 0, 0);
529         } else {
530                 error = 0;
531         }
532
533         /*
534          * Bump nlinks to synchronize with frontend.
535          */
536         if (xop->nlinks_delta) {
537                 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
538                 chain->data->ipdata.meta.nlinks += xop->nlinks_delta;
539         }
540
541         /*
542          * To avoid having to scan the collision space we can simply
543          * reuse the inode's original name_key.  But ip->meta.name_key
544          * may have already been updated by the front-end, so use xop->lhc.
545          *
546          * (frontend is responsible for fixing up ip->pip).
547          */
548 done:
549         hammer2_xop_feed(&xop->head, NULL, clindex, error);
550         if (parent) {
551                 hammer2_chain_unlock(parent);
552                 hammer2_chain_drop(parent);
553         }
554         if (chain) {
555                 hammer2_chain_unlock(chain);
556                 hammer2_chain_drop(chain);
557         }
558 }
559
560 /*
561  * Backend for hammer2_vop_nrename()
562  *
563  * This handles the final step of renaming, either renaming the
564  * actual inode or renaming the hardlink pointer.
565  */
566 void
567 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
568 {
569         hammer2_xop_nrename_t *xop = &arg->xop_nrename;
570         hammer2_pfs_t *pmp;
571         hammer2_chain_t *parent;
572         hammer2_chain_t *chain;
573         hammer2_chain_t *tmp;
574         hammer2_inode_t *ip;
575         hammer2_key_t key_dummy;
576         int cache_index = -1;
577         int error;
578
579         /*
580          * We need the precise parent chain to issue the deletion.
581          *
582          * If this is not a hardlink target we can act on the inode,
583          * otherwise we have to locate the hardlink pointer.
584          */
585         ip = xop->head.ip2;
586         pmp = ip->pmp;
587         chain = NULL;
588
589         if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
590                 /*
591                  * Find ip's direct parent chain.
592                  */
593                 parent = hammer2_inode_chain(ip, clindex,
594                                              HAMMER2_RESOLVE_ALWAYS);
595                 if (parent)
596                         hammer2_chain_getparent(&parent,
597                                                 HAMMER2_RESOLVE_ALWAYS);
598                 if (parent == NULL) {
599                         error = EIO;
600                         goto done;
601                 }
602                 chain = hammer2_inode_chain(ip, clindex,
603                                             HAMMER2_RESOLVE_ALWAYS);
604                 if (chain == NULL) {
605                         error = EIO;
606                         goto done;
607                 }
608         } else {
609                 /*
610                  * The hardlink pointer for the head.ip1 hardlink target
611                  * is in fdip, do a namespace search.
612                  */
613                 const hammer2_inode_data_t *ripdata;
614                 hammer2_key_t lhc;
615                 hammer2_key_t key_next;
616                 const char *name;
617                 size_t name_len;
618
619                 parent = hammer2_inode_chain(xop->head.ip1, clindex,
620                                              HAMMER2_RESOLVE_ALWAYS);
621                 if (parent == NULL) {
622                         kprintf("xop_nrename: NULL parent\n");
623                         error = EIO;
624                         goto done;
625                 }
626                 name = xop->head.name1;
627                 name_len = xop->head.name1_len;
628
629                 /*
630                  * Lookup the directory entry
631                  */
632                 lhc = hammer2_dirhash(name, name_len);
633                 chain = hammer2_chain_lookup(&parent, &key_next,
634                                              lhc, lhc + HAMMER2_DIRHASH_LOMASK,
635                                              &cache_index,
636                                              HAMMER2_LOOKUP_ALWAYS);
637                 while (chain) {
638                         ripdata = &chain->data->ipdata;
639                         if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
640                             ripdata->meta.name_len == name_len &&
641                             bcmp(ripdata->filename, name, name_len) == 0) {
642                                 break;
643                         }
644                         chain = hammer2_chain_next(&parent, chain, &key_next,
645                                                    key_next,
646                                                    lhc + HAMMER2_DIRHASH_LOMASK,
647                                                    &cache_index,
648                                                    HAMMER2_LOOKUP_ALWAYS);
649                 }
650         }
651
652         if (chain == NULL) {
653                 /* XXX shouldn't happen, but does under fsstress */
654                 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\"  ENOENT\n",
655                         xop->head.name1,
656                         xop->head.name2);
657                 error = ENOENT;
658                 goto done;
659         }
660
661         /*
662          * Delete it, then create it in the new namespace.
663          */
664         hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
665         hammer2_chain_unlock(parent);
666         hammer2_chain_drop(parent);
667         parent = NULL;          /* safety */
668
669         /*
670          * Ok, back to the deleted chain.  We must reconnect this chain
671          * to tdir (ip3).  The chain (a real inode or a hardlink pointer)
672          * is not otherwise modified.
673          *
674          * Frontend is expected to replicate the same inode meta data
675          * modifications.
676          *
677          * NOTE!  This chain may not represent the actual inode, it
678          *        can be a hardlink pointer.
679          *
680          * XXX in-inode parent directory specification?
681          */
682         if (chain->data->ipdata.meta.name_key != xop->lhc ||
683             xop->head.name1_len != xop->head.name2_len ||
684             bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
685                 hammer2_inode_data_t *wipdata;
686
687                 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
688                 wipdata = &chain->data->ipdata;
689
690                 bzero(wipdata->filename, sizeof(wipdata->filename));
691                 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
692                 wipdata->meta.name_key = xop->lhc;
693                 wipdata->meta.name_len = xop->head.name2_len;
694         }
695
696         /*
697          * We must seek parent properly for the create.
698          */
699         parent = hammer2_inode_chain(xop->head.ip3, clindex,
700                                      HAMMER2_RESOLVE_ALWAYS);
701         if (parent == NULL) {
702                 error = EIO;
703                 goto done;
704         }
705         tmp = hammer2_chain_lookup(&parent, &key_dummy,
706                                    xop->lhc, xop->lhc,
707                                    &cache_index, 0);
708         if (tmp) {
709                 hammer2_chain_unlock(tmp);
710                 hammer2_chain_drop(tmp);
711                 error = EEXIST;
712                 goto done;
713         }
714
715         error = hammer2_chain_create(&parent, &chain, pmp,
716                                      xop->lhc, 0,
717                                      HAMMER2_BREF_TYPE_INODE,
718                                      HAMMER2_INODE_BYTES,
719                                      xop->head.mtid, 0, 0);
720         /*
721          * (frontend is responsible for fixing up ip->pip).
722          */
723 done:
724         hammer2_xop_feed(&xop->head, NULL, clindex, error);
725         if (parent) {
726                 hammer2_chain_unlock(parent);
727                 hammer2_chain_drop(parent);
728         }
729         if (chain) {
730                 hammer2_chain_unlock(chain);
731                 hammer2_chain_drop(chain);
732         }
733 }
734
735 /*
736  * Directory collision resolver scan helper (backend, threaded).
737  *
738  * Used by the inode create code to locate an unused lhc.
739  */
740 void
741 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex)
742 {
743         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
744         hammer2_chain_t *parent;
745         hammer2_chain_t *chain;
746         hammer2_key_t key_next;
747         int cache_index = -1;   /* XXX */
748         int error = 0;
749
750         parent = hammer2_inode_chain(xop->head.ip1, clindex,
751                                      HAMMER2_RESOLVE_ALWAYS |
752                                      HAMMER2_RESOLVE_SHARED);
753         if (parent == NULL) {
754                 kprintf("xop_nresolve: NULL parent\n");
755                 chain = NULL;
756                 error = EIO;
757                 goto done;
758         }
759
760         /*
761          * Lookup all possibly conflicting directory entries, the feed
762          * inherits the chain's lock so do not unlock it on the iteration.
763          */
764         chain = hammer2_chain_lookup(&parent, &key_next,
765                                      xop->lhc,
766                                      xop->lhc + HAMMER2_DIRHASH_LOMASK,
767                                      &cache_index,
768                                      HAMMER2_LOOKUP_ALWAYS |
769                                      HAMMER2_LOOKUP_SHARED);
770         while (chain) {
771                 error = hammer2_xop_feed(&xop->head, chain, clindex,
772                                          chain->error);
773                 if (error) {
774                         hammer2_chain_drop(chain);
775                         chain = NULL;   /* safety */
776                         break;
777                 }
778                 chain = hammer2_chain_next(&parent, chain, &key_next,
779                                            key_next,
780                                            xop->lhc + HAMMER2_DIRHASH_LOMASK,
781                                            &cache_index,
782                                            HAMMER2_LOOKUP_ALWAYS |
783                                            HAMMER2_LOOKUP_SHARED |
784                                            HAMMER2_LOOKUP_NOUNLOCK);
785         }
786 done:
787         hammer2_xop_feed(&xop->head, NULL, clindex, error);
788         if (parent) {
789                 hammer2_chain_unlock(parent);
790                 hammer2_chain_drop(parent);
791         }
792 }
793
794 /*
795  * Generic lookup of a specific key.
796  *
797  * Used by the inode hidden directory code to find the hidden directory.
798  */
799 void
800 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex)
801 {
802         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
803         hammer2_chain_t *parent;
804         hammer2_chain_t *chain;
805         hammer2_key_t key_next;
806         int cache_index = -1;   /* XXX */
807         int error = 0;
808
809         parent = hammer2_inode_chain(xop->head.ip1, clindex,
810                                      HAMMER2_RESOLVE_ALWAYS |
811                                      HAMMER2_RESOLVE_SHARED);
812         chain = NULL;
813         if (parent == NULL) {
814                 error = EIO;
815                 goto done;
816         }
817
818         /*
819          * Lookup all possibly conflicting directory entries, the feed
820          * inherits the chain's lock so do not unlock it on the iteration.
821          */
822         chain = hammer2_chain_lookup(&parent, &key_next,
823                                      xop->lhc, xop->lhc,
824                                      &cache_index,
825                                      HAMMER2_LOOKUP_ALWAYS |
826                                      HAMMER2_LOOKUP_SHARED);
827         if (chain)
828                 hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
829         else
830                 hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT);
831
832 done:
833         if (chain) {
834                 /* leave lock intact for feed */
835                 hammer2_chain_drop(chain);
836         }
837         if (parent) {
838                 hammer2_chain_unlock(parent);
839                 hammer2_chain_drop(parent);
840         }
841 }
842
843 /*
844  * Generic scan
845  */
846 void
847 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex)
848 {
849         hammer2_xop_scanall_t *xop = &arg->xop_scanall;
850         hammer2_chain_t *parent;
851         hammer2_chain_t *chain;
852         hammer2_key_t key_next;
853         int cache_index = -1;
854         int error = 0;
855
856         /*
857          * The inode's chain is the iterator.  If we cannot acquire it our
858          * contribution ends here.
859          */
860         parent = hammer2_inode_chain(xop->head.ip1, clindex,
861                                      HAMMER2_RESOLVE_ALWAYS |
862                                      HAMMER2_RESOLVE_SHARED);
863         if (parent == NULL) {
864                 kprintf("xop_readdir: NULL parent\n");
865                 goto done;
866         }
867
868         /*
869          * Generic scan of exact records.  Note that indirect blocks are
870          * automatically recursed and will not be returned.
871          */
872         chain = hammer2_chain_lookup(&parent, &key_next,
873                                      xop->key_beg, xop->key_end,
874                                      &cache_index, HAMMER2_LOOKUP_SHARED |
875                                                    HAMMER2_LOOKUP_NODIRECT);
876         while (chain) {
877                 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
878                 if (error)
879                         break;
880                 chain = hammer2_chain_next(&parent, chain, &key_next,
881                                            key_next, xop->key_end,
882                                            &cache_index,
883                                            HAMMER2_LOOKUP_SHARED |
884                                            HAMMER2_LOOKUP_NODIRECT |
885                                            HAMMER2_LOOKUP_NOUNLOCK);
886         }
887         if (chain)
888                 hammer2_chain_drop(chain);
889         hammer2_chain_unlock(parent);
890         hammer2_chain_drop(parent);
891 done:
892         hammer2_xop_feed(&xop->head, NULL, clindex, error);
893 }