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