Merge branch 'vendor/NVI2'
[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.ip, clindex,
77                                     HAMMER2_RESOLVE_ALWAYS |
78                                     HAMMER2_RESOLVE_SHARED);
79         if (chain)
80                 error = chain->error;
81         else
82                 error = EIO;
83                 
84         hammer2_xop_feed(&xop->head, chain, clindex, error);
85         if (chain)
86                 hammer2_chain_drop(chain);
87 }
88
89 /*
90  * Backend for hammer2_vop_readdir()
91  */
92 void
93 hammer2_xop_readdir(hammer2_xop_t *arg, int clindex)
94 {
95         hammer2_xop_readdir_t *xop = &arg->xop_readdir;
96         hammer2_chain_t *parent;
97         hammer2_chain_t *chain;
98         hammer2_key_t key_next;
99         hammer2_key_t lkey;
100         int cache_index = -1;
101         int error = 0;
102
103         lkey = xop->lkey;
104         if (hammer2_debug & 0x0020)
105                 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
106
107         /*
108          * The inode's chain is the iterator.  If we cannot acquire it our
109          * contribution ends here.
110          */
111         parent = hammer2_inode_chain(xop->head.ip, clindex,
112                                      HAMMER2_RESOLVE_ALWAYS |
113                                      HAMMER2_RESOLVE_SHARED);
114         if (parent == NULL) {
115                 kprintf("xop_readdir: NULL parent\n");
116                 goto done;
117         }
118
119         /*
120          * Directory scan [re]start and loop, the feed inherits the chain's
121          * lock so do not unlock it on the iteration.
122          */
123         chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey,
124                                      &cache_index, HAMMER2_LOOKUP_SHARED);
125         if (chain == NULL) {
126                 chain = hammer2_chain_lookup(&parent, &key_next,
127                                              lkey, HAMMER2_KEY_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.ip, clindex,
167                                      HAMMER2_RESOLVE_ALWAYS |
168                                      HAMMER2_RESOLVE_SHARED);
169         if (parent == NULL) {
170                 kprintf("xop_nresolve: NULL parent\n");
171                 chain = NULL;
172                 error = EIO;
173                 goto done;
174         }
175         name = xop->head.name;
176         name_len = xop->head.name_len;
177
178         /*
179          * Lookup the directory entry
180          */
181         lhc = hammer2_dirhash(name, name_len);
182         chain = hammer2_chain_lookup(&parent, &key_next,
183                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
184                                      &cache_index,
185                                      HAMMER2_LOOKUP_ALWAYS |
186                                      HAMMER2_LOOKUP_SHARED);
187         while (chain) {
188                 ripdata = &chain->data->ipdata;
189                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
190                     ripdata->meta.name_len == name_len &&
191                     bcmp(ripdata->filename, name, name_len) == 0) {
192                         break;
193                 }
194                 chain = hammer2_chain_next(&parent, chain, &key_next,
195                                            key_next,
196                                            lhc + HAMMER2_DIRHASH_LOMASK,
197                                            &cache_index,
198                                            HAMMER2_LOOKUP_ALWAYS |
199                                            HAMMER2_LOOKUP_SHARED);
200         }
201
202         /*
203          * If the entry is a hardlink pointer, resolve it.
204          */
205         error = 0;
206         if (chain) {
207                 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
208                         error = hammer2_chain_hardlink_find(
209                                                 xop->head.ip,
210                                                 &parent, &chain,
211                                                 HAMMER2_RESOLVE_SHARED);
212                 }
213         }
214 done:
215         error = hammer2_xop_feed(&xop->head, chain, clindex, error);
216         if (chain) {
217                 /* leave lock intact for feed */
218                 hammer2_chain_drop(chain);
219         }
220         if (parent) {
221                 hammer2_chain_unlock(parent);
222                 hammer2_chain_drop(parent);
223         }
224 }
225
226 /*
227  * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper
228  * for hammer2_vop_nrename().
229  *
230  * This function does locates and removes the directory entry.  If the
231  * entry is a hardlink pointer, this function will also remove the
232  * hardlink target if the target's nlinks is 1.
233  *
234  * The frontend is responsible for moving open inodes to the hidden directory
235  * and for decrementing nlinks.
236  */
237 void
238 hammer2_xop_unlink(hammer2_xop_t *arg, int clindex)
239 {
240         hammer2_xop_unlink_t *xop = &arg->xop_unlink;
241         hammer2_chain_t *parent;
242         hammer2_chain_t *chain;
243         const hammer2_inode_data_t *ripdata;
244         const char *name;
245         size_t name_len;
246         uint8_t type;
247         hammer2_key_t key_next;
248         hammer2_key_t lhc;
249         int cache_index = -1;   /* XXX */
250         int error;
251
252         /*
253          * Requires exclusive lock
254          */
255         parent = hammer2_inode_chain(xop->head.ip, clindex,
256                                      HAMMER2_RESOLVE_ALWAYS);
257         if (parent == NULL) {
258                 kprintf("xop_nresolve: NULL parent\n");
259                 chain = NULL;
260                 error = EIO;
261                 goto done;
262         }
263         name = xop->head.name;
264         name_len = xop->head.name_len;
265
266         /*
267          * Lookup the directory entry
268          */
269         lhc = hammer2_dirhash(name, name_len);
270         chain = hammer2_chain_lookup(&parent, &key_next,
271                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
272                                      &cache_index,
273                                      HAMMER2_LOOKUP_ALWAYS);
274         while (chain) {
275                 ripdata = &chain->data->ipdata;
276                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
277                     ripdata->meta.name_len == name_len &&
278                     bcmp(ripdata->filename, name, name_len) == 0) {
279                         break;
280                 }
281                 chain = hammer2_chain_next(&parent, chain, &key_next,
282                                            key_next,
283                                            lhc + HAMMER2_DIRHASH_LOMASK,
284                                            &cache_index,
285                                            HAMMER2_LOOKUP_ALWAYS);
286         }
287
288         /*
289          * If the directory entry is a HARDLINK pointer then obtain the
290          * underlying file type for the directory typing tests and delete
291          * the HARDLINK pointer chain permanently.  The frontend is left
292          * responsible for handling nlinks on and deleting the actual inode.
293          *
294          * If the directory entry is the actual inode then use its type
295          * for the directory typing tests and delete the chain, permanency
296          * depends on whether the inode is open or not.
297          *
298          * Check directory typing and delete the entry.  Note that
299          * nlinks adjustments are made on the real inode by the frontend,
300          * not here.
301          */
302         error = 0;
303         if (chain) {
304                 int dopermanent = xop->dopermanent;
305
306                 type = chain->data->ipdata.meta.type;
307                 if (type == HAMMER2_OBJTYPE_HARDLINK) {
308                         type = chain->data->ipdata.meta.target_type;
309                         dopermanent |= HAMMER2_DELETE_PERMANENT;
310                 }
311                 if (type == HAMMER2_OBJTYPE_DIRECTORY &&
312                     xop->isdir == 0) {
313                         error = ENOTDIR;
314                 } else 
315                 if (type != HAMMER2_OBJTYPE_DIRECTORY &&
316                     xop->isdir >= 1) {
317                         error = EISDIR;
318                 } else {
319                         hammer2_chain_delete(parent, chain,
320                                              xop->head.mtid, xop->dopermanent);
321                 }
322         }
323
324         /*
325          * If the entry is a hardlink pointer, resolve it.  If this is the
326          * last link, delete it.  We aren't the frontend so we can't adjust
327          * nlinks.
328          */
329         if (chain) {
330                 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
331                         error = hammer2_chain_hardlink_find(
332                                                 xop->head.ip,
333                                                 &parent, &chain,
334                                                 0);
335                         if (chain &&
336                             (int64_t)chain->data->ipdata.meta.nlinks <= 1) {
337                                 hammer2_chain_delete(parent, chain,
338                                                      xop->head.mtid,
339                                                      xop->dopermanent);
340                         }
341                 }
342         }
343
344         /*
345          * Chains passed to feed are expected to be locked shared.
346          */
347         if (chain) {
348                 hammer2_chain_unlock(chain);
349                 hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS |
350                                           HAMMER2_RESOLVE_SHARED);
351         }
352
353         /*
354          * We always return the hardlink target (the real inode) for
355          * further action.
356          */
357 done:
358         hammer2_xop_feed(&xop->head, chain, clindex, error);
359         if (chain)
360                 hammer2_chain_drop(chain);
361         if (parent) {
362                 hammer2_chain_unlock(parent);
363                 hammer2_chain_drop(parent);
364         }
365 }
366
367 /*
368  * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
369  *
370  * Convert the target {dip,ip} to a hardlink target and replace
371  * the original namespace with a hardlink pointer.
372  */
373 void
374 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex)
375 {
376         hammer2_xop_nlink_t *xop = &arg->xop_nlink;
377         hammer2_pfs_t *pmp;
378         hammer2_inode_data_t *wipdata;
379         hammer2_chain_t *parent;
380         hammer2_chain_t *chain;
381         hammer2_chain_t *tmp;
382         hammer2_inode_t *ip;
383         hammer2_key_t key_dummy;
384         int cache_index = -1;
385         int error;
386
387         /*
388          * We need the precise parent chain to issue the deletion.
389          */
390         ip = xop->head.ip2;
391         pmp = ip->pmp;
392         parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
393         if (parent)
394                 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
395         if (parent == NULL) {
396                 chain = NULL;
397                 error = EIO;
398                 goto done;
399         }
400         chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
401         if (chain == NULL) {
402                 error = EIO;
403                 goto done;
404         }
405         hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
406
407         /*
408          * Replace the namespace with a hardlink pointer if the chain being
409          * moved is not already a hardlink target.
410          */
411         if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
412                 tmp = NULL;
413                 error = hammer2_chain_create(&parent, &tmp, pmp,
414                                              chain->bref.key, 0,
415                                              HAMMER2_BREF_TYPE_INODE,
416                                              HAMMER2_INODE_BYTES,
417                                              xop->head.mtid, 0);
418                 if (error)
419                         goto done;
420                 hammer2_chain_modify(tmp, xop->head.mtid, 0);
421                 wipdata = &tmp->data->ipdata;
422                 bzero(wipdata, sizeof(*wipdata));
423                 wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
424                 wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
425                 bcopy(chain->data->ipdata.filename, wipdata->filename,
426                       chain->data->ipdata.meta.name_len);
427                 wipdata->meta.target_type = chain->data->ipdata.meta.type;
428                 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
429                 wipdata->meta.inum = ip->meta.inum;
430                 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
431                 wipdata->meta.nlinks = 1;
432                 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
433
434                 hammer2_chain_unlock(tmp);
435                 hammer2_chain_drop(tmp);
436         }
437
438         hammer2_chain_unlock(parent);
439         hammer2_chain_drop(parent);
440
441         /*
442          * Ok, back to the deleted chain.  We must reconnect this chain
443          * as a hardlink target to cdir (ip3).
444          *
445          * WARNING! Frontend assumes filename length is 18 bytes.
446          */
447         hammer2_chain_modify(chain, xop->head.mtid, 0);
448         wipdata = &chain->data->ipdata;
449         ksnprintf(wipdata->filename, sizeof(wipdata->filename),
450                   "0x%016jx", (intmax_t)ip->meta.inum);
451         wipdata->meta.name_len = strlen(wipdata->filename);
452         wipdata->meta.name_key = ip->meta.inum;
453
454         /*
455          * We must seek parent properly for the create.
456          */
457         parent = hammer2_inode_chain(xop->head.ip3, clindex,
458                                      HAMMER2_RESOLVE_ALWAYS);
459         if (parent == NULL) {
460                 error = EIO;
461                 goto done;
462         }
463         tmp = hammer2_chain_lookup(&parent, &key_dummy,
464                                    ip->meta.inum, ip->meta.inum,
465                                    &cache_index, 0);
466         if (tmp) {
467                 hammer2_chain_unlock(tmp);
468                 hammer2_chain_drop(tmp);
469                 error = EEXIST;
470                 goto done;
471         }
472         error = hammer2_chain_create(&parent, &chain, pmp,
473                                      wipdata->meta.name_key, 0,
474                                      HAMMER2_BREF_TYPE_INODE,
475                                      HAMMER2_INODE_BYTES,
476                                      xop->head.mtid, 0);
477         /*
478          * To avoid having to scan the collision space we can simply
479          * reuse the inode's original name_key.  But ip->meta.name_key
480          * may have already been updated by the front-end, so use xop->lhc.
481          *
482          * (frontend is responsible for fixing up ip->pip).
483          */
484 done:
485         hammer2_xop_feed(&xop->head, NULL, clindex, error);
486         if (parent) {
487                 hammer2_chain_unlock(parent);
488                 hammer2_chain_drop(parent);
489         }
490         if (chain) {
491                 hammer2_chain_unlock(chain);
492                 hammer2_chain_drop(chain);
493         }
494 }
495
496 /*
497  * Backend for hammer2_vop_nrename()
498  *
499  * This handles the final step of renaming, either renaming the
500  * actual inode or renaming the hardlink pointer.
501  */
502 void
503 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
504 {
505         hammer2_xop_nrename_t *xop = &arg->xop_nrename;
506         hammer2_pfs_t *pmp;
507         hammer2_chain_t *parent;
508         hammer2_chain_t *chain;
509         hammer2_chain_t *tmp;
510         hammer2_inode_t *ip;
511         hammer2_key_t key_dummy;
512         int cache_index = -1;
513         int error;
514
515         /*
516          * We need the precise parent chain to issue the deletion.
517          *
518          * If this is not a hardlink target we can act on the inode,
519          * otherwise we have to locate the hardlink pointer.
520          */
521         ip = xop->head.ip2;
522         pmp = ip->pmp;
523         chain = NULL;
524
525         if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
526                 /*
527                  * Find ip's direct parent chain.
528                  */
529                 parent = hammer2_inode_chain(ip, clindex,
530                                              HAMMER2_RESOLVE_ALWAYS);
531                 if (parent)
532                         hammer2_chain_getparent(&parent,
533                                                 HAMMER2_RESOLVE_ALWAYS);
534                 if (parent == NULL) {
535                         error = EIO;
536                         goto done;
537                 }
538                 chain = hammer2_inode_chain(ip, clindex,
539                                             HAMMER2_RESOLVE_ALWAYS);
540                 if (chain == NULL) {
541                         error = EIO;
542                         goto done;
543                 }
544         } else {
545                 /*
546                  * head.ip is fdip, do a namespace search.
547                  */
548                 const hammer2_inode_data_t *ripdata;
549                 hammer2_key_t lhc;
550                 hammer2_key_t key_next;
551                 const char *name;
552                 size_t name_len;
553
554                 parent = hammer2_inode_chain(xop->head.ip, clindex,
555                                              HAMMER2_RESOLVE_ALWAYS |
556                                              HAMMER2_RESOLVE_SHARED);
557                 if (parent == NULL) {
558                         kprintf("xop_nrename: NULL parent\n");
559                         error = EIO;
560                         goto done;
561                 }
562                 name = xop->head.name;
563                 name_len = xop->head.name_len;
564
565                 /*
566                  * Lookup the directory entry
567                  */
568                 lhc = hammer2_dirhash(name, name_len);
569                 chain = hammer2_chain_lookup(&parent, &key_next,
570                                              lhc, lhc + HAMMER2_DIRHASH_LOMASK,
571                                              &cache_index,
572                                              HAMMER2_LOOKUP_ALWAYS);
573                 while (chain) {
574                         ripdata = &chain->data->ipdata;
575                         if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
576                             ripdata->meta.name_len == name_len &&
577                             bcmp(ripdata->filename, name, name_len) == 0) {
578                                 break;
579                         }
580                         chain = hammer2_chain_next(&parent, chain, &key_next,
581                                                    key_next,
582                                                    lhc + HAMMER2_DIRHASH_LOMASK,
583                                                    &cache_index,
584                                                    HAMMER2_LOOKUP_ALWAYS);
585                 }
586         }
587
588         /*
589          * Delete it, then create it in the new namespace.
590          */
591         hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
592         hammer2_chain_unlock(parent);
593         hammer2_chain_drop(parent);
594         parent = NULL;          /* safety */
595
596
597         /*
598          * Ok, back to the deleted chain.  We must reconnect this chain
599          * to tdir (ip3).  The chain (a real inode or a hardlink pointer)
600          * is not otherwise modified.
601          *
602          * Frontend is expected to replicate the same inode meta data
603          * modifications.
604          *
605          * NOTE!  This chain may not represent the actual inode, it
606          *        can be a hardlink pointer.
607          *
608          * XXX in-inode parent directory specification?
609          */
610         if (chain->data->ipdata.meta.name_key != xop->lhc ||
611             xop->head.name_len != xop->head.name2_len ||
612             bcmp(xop->head.name, xop->head.name2, xop->head.name_len) != 0) {
613                 hammer2_inode_data_t *wipdata;
614
615                 hammer2_chain_modify(chain, xop->head.mtid, 0);
616                 wipdata = &chain->data->ipdata;
617
618                 bzero(wipdata->filename, sizeof(wipdata->filename));
619                 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
620                 wipdata->meta.name_key = xop->lhc;
621                 wipdata->meta.name_len = xop->head.name2_len;
622         }
623
624         /*
625          * We must seek parent properly for the create.
626          */
627         parent = hammer2_inode_chain(xop->head.ip3, clindex,
628                                      HAMMER2_RESOLVE_ALWAYS);
629         if (parent == NULL) {
630                 error = EIO;
631                 goto done;
632         }
633         tmp = hammer2_chain_lookup(&parent, &key_dummy,
634                                    xop->lhc, xop->lhc,
635                                    &cache_index, 0);
636         if (tmp) {
637                 hammer2_chain_unlock(tmp);
638                 hammer2_chain_drop(tmp);
639                 error = EEXIST;
640                 goto done;
641         }
642
643         error = hammer2_chain_create(&parent, &chain, pmp,
644                                      xop->lhc, 0,
645                                      HAMMER2_BREF_TYPE_INODE,
646                                      HAMMER2_INODE_BYTES,
647                                      xop->head.mtid, 0);
648         /*
649          * (frontend is responsible for fixing up ip->pip).
650          */
651 done:
652         hammer2_xop_feed(&xop->head, NULL, clindex, error);
653         if (parent) {
654                 hammer2_chain_unlock(parent);
655                 hammer2_chain_drop(parent);
656         }
657         if (chain) {
658                 hammer2_chain_unlock(chain);
659                 hammer2_chain_drop(chain);
660         }
661 }
662
663 /*
664  * Directory collision resolver scan helper (backend, threaded).
665  *
666  * Used by the inode create code to locate an unused lhc.
667  */
668 void
669 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex)
670 {
671         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
672         hammer2_chain_t *parent;
673         hammer2_chain_t *chain;
674         hammer2_key_t key_next;
675         int cache_index = -1;   /* XXX */
676         int error = 0;
677
678         parent = hammer2_inode_chain(xop->head.ip, clindex,
679                                      HAMMER2_RESOLVE_ALWAYS |
680                                      HAMMER2_RESOLVE_SHARED);
681         if (parent == NULL) {
682                 kprintf("xop_nresolve: NULL parent\n");
683                 chain = NULL;
684                 error = EIO;
685                 goto done;
686         }
687
688         /*
689          * Lookup all possibly conflicting directory entries, the feed
690          * inherits the chain's lock so do not unlock it on the iteration.
691          */
692         chain = hammer2_chain_lookup(&parent, &key_next,
693                                      xop->lhc,
694                                      xop->lhc + HAMMER2_DIRHASH_LOMASK,
695                                      &cache_index,
696                                      HAMMER2_LOOKUP_ALWAYS |
697                                      HAMMER2_LOOKUP_SHARED);
698         while (chain) {
699                 error = hammer2_xop_feed(&xop->head, chain, clindex,
700                                          chain->error);
701                 if (error) {
702                         hammer2_chain_drop(chain);
703                         chain = NULL;   /* safety */
704                         break;
705                 }
706                 chain = hammer2_chain_next(&parent, chain, &key_next,
707                                            key_next,
708                                            xop->lhc + HAMMER2_DIRHASH_LOMASK,
709                                            &cache_index,
710                                            HAMMER2_LOOKUP_ALWAYS |
711                                            HAMMER2_LOOKUP_SHARED |
712                                            HAMMER2_LOOKUP_NOUNLOCK);
713         }
714 done:
715         hammer2_xop_feed(&xop->head, NULL, clindex, error);
716         if (parent) {
717                 hammer2_chain_unlock(parent);
718                 hammer2_chain_drop(parent);
719         }
720 }
721
722 /*
723  * Generic lookup of a specific key.
724  *
725  * Used by the inode hidden directory code to find the hidden directory.
726  */
727 void
728 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex)
729 {
730         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
731         hammer2_chain_t *parent;
732         hammer2_chain_t *chain;
733         hammer2_key_t key_next;
734         int cache_index = -1;   /* XXX */
735         int error = 0;
736
737         parent = hammer2_inode_chain(xop->head.ip, clindex,
738                                      HAMMER2_RESOLVE_ALWAYS |
739                                      HAMMER2_RESOLVE_SHARED);
740         chain = NULL;
741         if (parent == NULL) {
742                 error = EIO;
743                 goto done;
744         }
745
746         /*
747          * Lookup all possibly conflicting directory entries, the feed
748          * inherits the chain's lock so do not unlock it on the iteration.
749          */
750         chain = hammer2_chain_lookup(&parent, &key_next,
751                                      xop->lhc, xop->lhc,
752                                      &cache_index,
753                                      HAMMER2_LOOKUP_ALWAYS |
754                                      HAMMER2_LOOKUP_SHARED);
755         if (chain)
756                 hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
757         else
758                 hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT);
759
760 done:
761         if (chain) {
762                 /* leave lock intact for feed */
763                 hammer2_chain_drop(chain);
764         }
765         if (parent) {
766                 hammer2_chain_unlock(parent);
767                 hammer2_chain_drop(parent);
768         }
769 }
770
771 /*
772  * Generic scan
773  */
774 void
775 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex)
776 {
777         hammer2_xop_scanall_t *xop = &arg->xop_scanall;
778         hammer2_chain_t *parent;
779         hammer2_chain_t *chain;
780         hammer2_key_t key_next;
781         int cache_index = -1;
782         int error = 0;
783
784         /*
785          * The inode's chain is the iterator.  If we cannot acquire it our
786          * contribution ends here.
787          */
788         parent = hammer2_inode_chain(xop->head.ip, clindex,
789                                      HAMMER2_RESOLVE_ALWAYS |
790                                      HAMMER2_RESOLVE_SHARED);
791         if (parent == NULL) {
792                 kprintf("xop_readdir: NULL parent\n");
793                 goto done;
794         }
795
796         /*
797          * Generic scan of exact records.  Note that indirect blocks are
798          * automatically recursed and will not be returned.
799          */
800         chain = hammer2_chain_lookup(&parent, &key_next,
801                                      xop->key_beg, xop->key_end,
802                                      &cache_index, HAMMER2_LOOKUP_SHARED |
803                                                    HAMMER2_LOOKUP_NODIRECT);
804         while (chain) {
805                 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
806                 if (error)
807                         break;
808                 chain = hammer2_chain_next(&parent, chain, &key_next,
809                                            key_next, xop->key_end,
810                                            &cache_index,
811                                            HAMMER2_LOOKUP_SHARED |
812                                            HAMMER2_LOOKUP_NODIRECT |
813                                            HAMMER2_LOOKUP_NOUNLOCK);
814         }
815         if (chain)
816                 hammer2_chain_drop(chain);
817         hammer2_chain_unlock(parent);
818         hammer2_chain_drop(parent);
819 done:
820         hammer2_xop_feed(&xop->head, NULL, clindex, error);
821 }