sys/vfs/hammer2: Adjust some kprintfs in vfsops
[dragonfly.git] / sys / vfs / hammer2 / hammer2_xops.c
1 /*
2  * Copyright (c) 2011-2018 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/mount.h>
51 #include <sys/vnode.h>
52 #include <sys/mountctl.h>
53 #include <sys/dirent.h>
54 #include <sys/uio.h>
55 #include <sys/objcache.h>
56 #include <sys/event.h>
57 #include <sys/file.h>
58 #include <vfs/fifofs/fifo.h>
59
60 #include "hammer2.h"
61
62 /*
63  * Determine if the specified directory is empty.
64  *
65  *      Returns 0 on success.
66  *
67  *      Returns HAMMER_ERROR_EAGAIN if caller must re-lookup the entry and
68  *      retry. (occurs if we race a ripup on oparent or ochain).
69  *
70  *      Or returns a permanent HAMMER2_ERROR_* error mask.
71  *
72  * The caller must pass in an exclusively locked oparent and ochain.  This
73  * function will handle the case where the chain is a directory entry or
74  * the inode itself.  The original oparent,ochain will be locked upon return.
75  *
76  * This function will unlock the underlying oparent,ochain temporarily when
77  * doing an inode lookup to avoid deadlocks.  The caller MUST handle the EAGAIN
78  * result as this means that oparent is no longer the parent of ochain, or
79  * that ochain was destroyed while it was unlocked.
80  */
81 static
82 int
83 checkdirempty(hammer2_chain_t *oparent, hammer2_chain_t *ochain, int clindex)
84 {
85         hammer2_chain_t *parent;
86         hammer2_chain_t *chain;
87         hammer2_key_t key_next;
88         hammer2_key_t inum;
89         int error;
90         int didunlock;
91
92         error = 0;
93         didunlock = 0;
94
95         /*
96          * Find the inode, set it up as a locked 'chain'.  ochain can be the
97          * inode itself, or it can be a directory entry.
98          */
99         if (ochain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
100                 inum = ochain->bref.embed.dirent.inum;
101                 hammer2_chain_unlock(ochain);
102                 hammer2_chain_unlock(oparent);
103
104                 parent = NULL;
105                 chain = NULL;
106                 error = hammer2_chain_inode_find(ochain->pmp, inum,
107                                                  clindex, 0,
108                                                  &parent, &chain);
109                 if (parent) {
110                         hammer2_chain_unlock(parent);
111                         hammer2_chain_drop(parent);
112                 }
113                 didunlock = 1;
114         } else {
115                 /*
116                  * The directory entry *is* the directory inode
117                  */
118                 chain = hammer2_chain_lookup_init(ochain, 0);
119         }
120
121         /*
122          * Determine if the directory is empty or not by checking its
123          * visible namespace (the area which contains directory entries).
124          */
125         if (error == 0) {
126                 parent = chain;
127                 chain = NULL;
128                 if (parent) {
129                         chain = hammer2_chain_lookup(&parent, &key_next,
130                                                      HAMMER2_DIRHASH_VISIBLE,
131                                                      HAMMER2_KEY_MAX,
132                                                      &error, 0);
133                 }
134                 if (chain) {
135                         error = HAMMER2_ERROR_ENOTEMPTY;
136                         hammer2_chain_unlock(chain);
137                         hammer2_chain_drop(chain);
138                 }
139                 hammer2_chain_lookup_done(parent);
140         }
141
142         if (didunlock) {
143                 hammer2_chain_lock(oparent, HAMMER2_RESOLVE_ALWAYS);
144                 hammer2_chain_lock(ochain, HAMMER2_RESOLVE_ALWAYS);
145                 if ((ochain->flags & HAMMER2_CHAIN_DELETED) ||
146                     (oparent->flags & HAMMER2_CHAIN_DELETED) ||
147                     ochain->parent != oparent) {
148                         kprintf("hammer2: debug: CHECKDIR inum %jd RETRY\n",
149                                 inum);
150                         error = HAMMER2_ERROR_EAGAIN;
151                 }
152         }
153         return error;
154 }
155
156 /*
157  * Backend for hammer2_vfs_root()
158  *
159  * This is called when a newly mounted PFS has not yet synchronized
160  * to the inode_tid and modify_tid.
161  */
162 void
163 hammer2_xop_ipcluster(hammer2_xop_t *arg, void *scratch, int clindex)
164 {
165         hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster;
166         hammer2_chain_t *chain;
167         int error;
168
169         chain = hammer2_inode_chain(xop->head.ip1, clindex,
170                                     HAMMER2_RESOLVE_ALWAYS |
171                                     HAMMER2_RESOLVE_SHARED);
172         if (chain)
173                 error = chain->error;
174         else
175                 error = HAMMER2_ERROR_EIO;
176                 
177         hammer2_xop_feed(&xop->head, chain, clindex, error);
178         if (chain) {
179                 hammer2_chain_unlock(chain);
180                 hammer2_chain_drop(chain);
181         }
182 }
183
184 /*
185  * Backend for hammer2_vop_readdir()
186  */
187 void
188 hammer2_xop_readdir(hammer2_xop_t *arg, void *scratch, int clindex)
189 {
190         hammer2_xop_readdir_t *xop = &arg->xop_readdir;
191         hammer2_chain_t *parent;
192         hammer2_chain_t *chain;
193         hammer2_key_t key_next;
194         hammer2_key_t lkey;
195         int error = 0;
196
197         lkey = xop->lkey;
198         if (hammer2_debug & 0x0020)
199                 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
200
201         /*
202          * The inode's chain is the iterator.  If we cannot acquire it our
203          * contribution ends here.
204          */
205         parent = hammer2_inode_chain(xop->head.ip1, clindex,
206                                      HAMMER2_RESOLVE_ALWAYS |
207                                      HAMMER2_RESOLVE_SHARED);
208         if (parent == NULL) {
209                 kprintf("xop_readdir: NULL parent\n");
210                 goto done;
211         }
212
213         /*
214          * Directory scan [re]start and loop, the feed inherits the chain's
215          * lock so do not unlock it on the iteration.
216          */
217         chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey,
218                                      &error, HAMMER2_LOOKUP_SHARED);
219         if (chain == NULL) {
220                 chain = hammer2_chain_lookup(&parent, &key_next,
221                                              lkey, HAMMER2_KEY_MAX,
222                                              &error, HAMMER2_LOOKUP_SHARED);
223         }
224         while (chain) {
225                 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
226                 if (error)
227                         goto break2;
228                 chain = hammer2_chain_next(&parent, chain, &key_next,
229                                            key_next, HAMMER2_KEY_MAX,
230                                            &error, HAMMER2_LOOKUP_SHARED);
231         }
232 break2:
233         if (chain) {
234                 hammer2_chain_unlock(chain);
235                 hammer2_chain_drop(chain);
236         }
237         hammer2_chain_unlock(parent);
238         hammer2_chain_drop(parent);
239 done:
240         hammer2_xop_feed(&xop->head, NULL, clindex, error);
241 }
242
243 /*
244  * Backend for hammer2_vop_nresolve()
245  */
246 void
247 hammer2_xop_nresolve(hammer2_xop_t *arg, void *scratch, int clindex)
248 {
249         hammer2_xop_nresolve_t *xop = &arg->xop_nresolve;
250         hammer2_chain_t *parent;
251         hammer2_chain_t *chain;
252         const char *name;
253         size_t name_len;
254         hammer2_key_t key_next;
255         hammer2_key_t lhc;
256         int error;
257
258         parent = hammer2_inode_chain(xop->head.ip1, clindex,
259                                      HAMMER2_RESOLVE_ALWAYS |
260                                      HAMMER2_RESOLVE_SHARED);
261         if (parent == NULL) {
262                 kprintf("xop_nresolve: NULL parent\n");
263                 chain = NULL;
264                 error = HAMMER2_ERROR_EIO;
265                 goto done;
266         }
267         name = xop->head.name1;
268         name_len = xop->head.name1_len;
269
270         /*
271          * Lookup the directory entry
272          */
273         lhc = hammer2_dirhash(name, name_len);
274         chain = hammer2_chain_lookup(&parent, &key_next,
275                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
276                                      &error,
277                                      HAMMER2_LOOKUP_ALWAYS |
278                                      HAMMER2_LOOKUP_SHARED);
279         while (chain) {
280                 if (hammer2_chain_dirent_test(chain, name, name_len))
281                         break;
282                 chain = hammer2_chain_next(&parent, chain, &key_next,
283                                            key_next,
284                                            lhc + HAMMER2_DIRHASH_LOMASK,
285                                            &error,
286                                            HAMMER2_LOOKUP_ALWAYS |
287                                            HAMMER2_LOOKUP_SHARED);
288         }
289
290         /*
291          * Locate the target inode for a directory entry
292          */
293         if (chain && chain->error == 0) {
294                 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
295                         lhc = chain->bref.embed.dirent.inum;
296                         error = hammer2_chain_inode_find(chain->pmp,
297                                                          lhc,
298                                                          clindex,
299                                                          HAMMER2_LOOKUP_SHARED,
300                                                          &parent,
301                                                          &chain);
302                 }
303         } else if (chain && error == 0) {
304                 error = chain->error;
305         }
306 done:
307         error = hammer2_xop_feed(&xop->head, chain, clindex, error);
308         if (chain) {
309                 hammer2_chain_unlock(chain);
310                 hammer2_chain_drop(chain);
311         }
312         if (parent) {
313                 hammer2_chain_unlock(parent);
314                 hammer2_chain_drop(parent);
315         }
316 }
317
318 /*
319  * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and
320  * backend for pfs_delete.
321  *
322  * This function locates and removes a directory entry, and will lookup
323  * and return the underlying inode.  For directory entries the underlying
324  * inode is not removed.  If the directory entry is the actual inode itself,
325  * it may be conditonally removed and returned.
326  *
327  * WARNING!  Any target inode's nlinks may not be synchronized to the
328  *           in-memory inode.  The frontend's hammer2_inode_unlink_finisher()
329  *           is responsible for the final disposition of the actual inode.
330  */
331 void
332 hammer2_xop_unlink(hammer2_xop_t *arg, void *scratch, int clindex)
333 {
334         hammer2_xop_unlink_t *xop = &arg->xop_unlink;
335         hammer2_chain_t *parent;
336         hammer2_chain_t *chain;
337         const char *name;
338         size_t name_len;
339         hammer2_key_t key_next;
340         hammer2_key_t lhc;
341         int error;
342
343 again:
344         /*
345          * Requires exclusive lock
346          */
347         parent = hammer2_inode_chain(xop->head.ip1, clindex,
348                                      HAMMER2_RESOLVE_ALWAYS);
349         chain = NULL;
350         if (parent == NULL) {
351                 kprintf("xop_nresolve: NULL parent\n");
352                 error = HAMMER2_ERROR_EIO;
353                 goto done;
354         }
355         name = xop->head.name1;
356         name_len = xop->head.name1_len;
357
358         /*
359          * Lookup the directory entry
360          */
361         lhc = hammer2_dirhash(name, name_len);
362         chain = hammer2_chain_lookup(&parent, &key_next,
363                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
364                                      &error, HAMMER2_LOOKUP_ALWAYS);
365         while (chain) {
366                 if (hammer2_chain_dirent_test(chain, name, name_len))
367                         break;
368                 chain = hammer2_chain_next(&parent, chain, &key_next,
369                                            key_next,
370                                            lhc + HAMMER2_DIRHASH_LOMASK,
371                                            &error, HAMMER2_LOOKUP_ALWAYS);
372         }
373
374         /*
375          * The directory entry will either be a BREF_TYPE_DIRENT or a
376          * BREF_TYPE_INODE.  We always permanently delete DIRENTs, but
377          * must go by xop->dopermanent for BREF_TYPE_INODE.
378          *
379          * Note that the target chain's nlinks may not be synchronized with
380          * the in-memory hammer2_inode_t structure, so we don't try to do
381          * anything fancy here.  The frontend deals with nlinks
382          * synchronization.
383          */
384         if (chain && chain->error == 0) {
385                 int dopermanent = xop->dopermanent & H2DOPERM_PERMANENT;
386                 int doforce = xop->dopermanent & H2DOPERM_FORCE;
387                 uint8_t type;
388
389                 /*
390                  * If the directory entry is the actual inode then use its
391                  * type for the directory typing tests, otherwise if it is
392                  * a directory entry, pull the type field from the entry.
393                  *
394                  * Directory entries are always permanently deleted
395                  * (because they aren't the actual inode).
396                  */
397                 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
398                         type = chain->bref.embed.dirent.type;
399                         dopermanent |= HAMMER2_DELETE_PERMANENT;
400                 } else {
401                         type = chain->data->ipdata.meta.type;
402                 }
403
404                 /*
405                  * Check directory typing and delete the entry.  Note that
406                  * nlinks adjustments are made on the real inode by the
407                  * frontend, not here.
408                  *
409                  * Unfortunately, checkdirempty() may have to unlock (parent).
410                  * If it no longer matches chain->parent after re-locking,
411                  * EAGAIN is returned.
412                  */
413                 if (type == HAMMER2_OBJTYPE_DIRECTORY && doforce) {
414                         /*
415                          * If doforce then execute the operation even if
416                          * the directory is not empty or errored.  We
417                          * ignore chain->error here, allowing an errored
418                          * chain (aka directory entry) to still be deleted.
419                          */
420                         error = hammer2_chain_delete(parent, chain,
421                                              xop->head.mtid, dopermanent);
422                 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
423                     xop->isdir == 0) {
424                         error = HAMMER2_ERROR_EISDIR;
425                 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
426                            (error = checkdirempty(parent, chain, clindex)) != 0) {
427                         /*
428                          * error may be EAGAIN or ENOTEMPTY
429                          */
430                         if (error == HAMMER2_ERROR_EAGAIN) {
431                                 hammer2_chain_unlock(chain);
432                                 hammer2_chain_drop(chain);
433                                 hammer2_chain_unlock(parent);
434                                 hammer2_chain_drop(parent);
435                                 goto again;
436                         }
437                 } else if (type != HAMMER2_OBJTYPE_DIRECTORY &&
438                            xop->isdir >= 1) {
439                         error = HAMMER2_ERROR_ENOTDIR;
440                 } else {
441                         /*
442                          * Delete the directory entry.  chain might also
443                          * be a directly-embedded inode.
444                          *
445                          * Allow the deletion to proceed even if the chain
446                          * is errored.  Give priority to error-on-delete over
447                          * chain->error.
448                          */
449                         error = hammer2_chain_delete(parent, chain,
450                                                      xop->head.mtid,
451                                                      dopermanent);
452                         if (error == 0)
453                                 error = chain->error;
454                 }
455         } else {
456                 if (chain && error == 0)
457                         error = chain->error;
458         }
459
460         /*
461          * If chain is a directory entry we must resolve it.  We do not try
462          * to manipulate the contents as it might not be synchronized with
463          * the frontend hammer2_inode_t, nor do we try to lookup the
464          * frontend hammer2_inode_t here (we are the backend!).
465          */
466         if (chain && chain->bref.type == HAMMER2_BREF_TYPE_DIRENT &&
467             (xop->dopermanent & H2DOPERM_IGNINO) == 0) {
468                 int error2;
469
470                 lhc = chain->bref.embed.dirent.inum;
471
472                 error2 = hammer2_chain_inode_find(chain->pmp, lhc,
473                                                   clindex, 0,
474                                                   &parent, &chain);
475                 if (error2) {
476                         kprintf("inode_find: %016jx %p failed\n",
477                                 lhc, chain);
478                         error2 = 0;     /* silently ignore */
479                 }
480                 if (error == 0)
481                         error = error2;
482         }
483
484         /*
485          * Return the inode target for further action.  Typically used by
486          * hammer2_inode_unlink_finisher().
487          */
488 done:
489         hammer2_xop_feed(&xop->head, chain, clindex, error);
490         if (chain) {
491                 hammer2_chain_unlock(chain);
492                 hammer2_chain_drop(chain);
493                 chain = NULL;
494         }
495         if (parent) {
496                 hammer2_chain_unlock(parent);
497                 hammer2_chain_drop(parent);
498                 parent = NULL;
499         }
500 }
501
502 /*
503  * Backend for hammer2_vop_nrename()
504  *
505  * This handles the backend rename operation.  Typically this renames
506  * directory entries but can also be used to rename embedded inodes.
507  *
508  * NOTE! The frontend is responsible for updating the inode meta-data in
509  *       the file being renamed and for decrementing the target-replaced
510  *       inode's nlinks, if present.
511  */
512 void
513 hammer2_xop_nrename(hammer2_xop_t *arg, void *scratch, int clindex)
514 {
515         hammer2_xop_nrename_t *xop = &arg->xop_nrename;
516         hammer2_pfs_t *pmp;
517         hammer2_chain_t *parent;
518         hammer2_chain_t *chain;
519         hammer2_chain_t *tmp;
520         hammer2_inode_t *ip;
521         hammer2_key_t key_next;
522         int error;
523
524         /*
525          * We need the precise parent chain to issue the deletion.
526          *
527          * If this is a directory entry we must locate the underlying
528          * inode.  If it is an embedded inode we can act directly on it.
529          */
530         ip = xop->head.ip2;
531         pmp = ip->pmp;
532         chain = NULL;
533         error = 0;
534
535         if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
536                 /*
537                  * Find ip's direct parent chain.
538                  */
539                 chain = hammer2_inode_chain(ip, clindex,
540                                             HAMMER2_RESOLVE_ALWAYS);
541                 if (chain == NULL) {
542                         error = HAMMER2_ERROR_EIO;
543                         parent = NULL;
544                         goto done;
545                 }
546                 if (ip->flags & HAMMER2_INODE_CREATING) {
547                         parent = NULL;
548                 } else {
549                         parent = hammer2_chain_getparent(chain,
550                                                     HAMMER2_RESOLVE_ALWAYS);
551                         if (parent == NULL) {
552                                 error = HAMMER2_ERROR_EIO;
553                                 goto done;
554                         }
555                 }
556         } else {
557                 /*
558                  * The directory entry for the head.ip1 inode
559                  * is in fdip, do a namespace search.
560                  */
561                 hammer2_key_t lhc;
562                 const char *name;
563                 size_t name_len;
564
565                 parent = hammer2_inode_chain(xop->head.ip1, clindex,
566                                              HAMMER2_RESOLVE_ALWAYS);
567                 if (parent == NULL) {
568                         kprintf("xop_nrename: NULL parent\n");
569                         error = HAMMER2_ERROR_EIO;
570                         goto done;
571                 }
572                 name = xop->head.name1;
573                 name_len = xop->head.name1_len;
574
575                 /*
576                  * Lookup the directory entry
577                  */
578                 lhc = hammer2_dirhash(name, name_len);
579                 chain = hammer2_chain_lookup(&parent, &key_next,
580                                              lhc, lhc + HAMMER2_DIRHASH_LOMASK,
581                                              &error, HAMMER2_LOOKUP_ALWAYS);
582                 while (chain) {
583                         if (hammer2_chain_dirent_test(chain, name, name_len))
584                                 break;
585                         chain = hammer2_chain_next(&parent, chain, &key_next,
586                                                    key_next,
587                                                    lhc + HAMMER2_DIRHASH_LOMASK,
588                                                    &error,
589                                                    HAMMER2_LOOKUP_ALWAYS);
590                 }
591         }
592
593         if (chain == NULL) {
594                 /* XXX shouldn't happen, but does under fsstress */
595                 kprintf("hammer2_xop_nrename: \"%s\" -> \"%s\"  ENOENT\n",
596                         xop->head.name1,
597                         xop->head.name2);
598                 if (error == 0)
599                         error = HAMMER2_ERROR_ENOENT;
600                 goto done;
601         }
602
603         if (chain->error) {
604                 error = chain->error;
605                 goto done;
606         }
607
608         /*
609          * Delete it, then create it in the new namespace.
610          *
611          * An error can occur if the chain being deleted requires
612          * modification and the media is full.
613          */
614         error = hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
615         hammer2_chain_unlock(parent);
616         hammer2_chain_drop(parent);
617         parent = NULL;          /* safety */
618         if (error)
619                 goto done;
620
621         /*
622          * Adjust fields in the deleted chain appropriate for the rename
623          * operation.
624          *
625          * NOTE! For embedded inodes, the frontend will officially replicate
626          *       the field adjustments, but we also do it here to maintain
627          *       consistency in case of a crash.
628          */
629         if (chain->bref.key != xop->lhc ||
630             xop->head.name1_len != xop->head.name2_len ||
631             bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
632                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
633                         hammer2_inode_data_t *wipdata;
634
635                         error = hammer2_chain_modify(chain, xop->head.mtid,
636                                                      0, 0);
637                         if (error == 0) {
638                                 wipdata = &chain->data->ipdata;
639
640                                 bzero(wipdata->filename,
641                                       sizeof(wipdata->filename));
642                                 bcopy(xop->head.name2,
643                                       wipdata->filename,
644                                       xop->head.name2_len);
645                                 wipdata->meta.name_key = xop->lhc;
646                                 wipdata->meta.name_len = xop->head.name2_len;
647                         }
648                 }
649                 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
650                         if (xop->head.name2_len <=
651                             sizeof(chain->bref.check.buf)) {
652                                 /*
653                                  * Remove any related data buffer, we can
654                                  * embed the filename in the bref itself.
655                                  */
656                                 error = hammer2_chain_resize(
657                                                 chain, xop->head.mtid, 0, 0, 0);
658                                 if (error == 0) {
659                                         error = hammer2_chain_modify(
660                                                         chain, xop->head.mtid,
661                                                         0, 0);
662                                 }
663                                 if (error == 0) {
664                                         bzero(chain->bref.check.buf,
665                                               sizeof(chain->bref.check.buf));
666                                         bcopy(xop->head.name2,
667                                               chain->bref.check.buf,
668                                               xop->head.name2_len);
669                                 }
670                         } else {
671                                 /*
672                                  * Associate a data buffer with the bref.
673                                  * Zero it for consistency.  Note that the
674                                  * data buffer is not 64KB so use chain->bytes
675                                  * instead of sizeof().
676                                  */
677                                 error = hammer2_chain_resize(
678                                         chain, xop->head.mtid, 0,
679                                         hammer2_getradix(HAMMER2_ALLOC_MIN), 0);
680                                 if (error == 0) {
681                                         error = hammer2_chain_modify(
682                                                     chain, xop->head.mtid,
683                                                     0, 0);
684                                 }
685                                 if (error == 0) {
686                                         bzero(chain->data->buf, chain->bytes);
687                                         bcopy(xop->head.name2,
688                                               chain->data->buf,
689                                               xop->head.name2_len);
690                                 }
691                         }
692                         if (error == 0) {
693                                 chain->bref.embed.dirent.namlen =
694                                         xop->head.name2_len;
695                         }
696                 }
697         }
698
699         /*
700          * The frontend will replicate this operation and is the real final
701          * authority, but adjust the inode's iparent field too if the inode
702          * is embedded in the directory.
703          */
704         if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
705             chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) {
706                 hammer2_inode_data_t *wipdata;
707
708                 error = hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
709                 if (error == 0) {
710                         wipdata = &chain->data->ipdata;
711                         wipdata->meta.iparent = xop->head.ip3->meta.inum;
712                 }
713         }
714
715         /*
716          * Destroy any matching target(s) before creating the new entry.
717          * This will result in some ping-ponging of the directory key
718          * iterator but that is ok.
719          */
720         parent = hammer2_inode_chain(xop->head.ip3, clindex,
721                                      HAMMER2_RESOLVE_ALWAYS);
722         if (parent == NULL) {
723                 error = HAMMER2_ERROR_EIO;
724                 goto done;
725         }
726
727         /*
728          * Delete all matching directory entries.  That is, get rid of
729          * multiple duplicates if present, as a self-healing mechanism.
730          */
731         if (error == 0) {
732                 tmp = hammer2_chain_lookup(&parent, &key_next,
733                                            xop->lhc & ~HAMMER2_DIRHASH_LOMASK,
734                                            xop->lhc | HAMMER2_DIRHASH_LOMASK,
735                                            &error,
736                                            HAMMER2_LOOKUP_ALWAYS);
737                 while (tmp) {
738                         int e2;
739                         if (hammer2_chain_dirent_test(tmp, xop->head.name2,
740                                                       xop->head.name2_len)) {
741                                 e2 = hammer2_chain_delete(parent, tmp,
742                                                           xop->head.mtid, 0);
743                                 if (error == 0 && e2)
744                                         error = e2;
745                         }
746                         tmp = hammer2_chain_next(&parent, tmp, &key_next,
747                                                  key_next,
748                                                  xop->lhc |
749                                                   HAMMER2_DIRHASH_LOMASK,
750                                                  &error,
751                                                  HAMMER2_LOOKUP_ALWAYS);
752                 }
753         }
754         if (error == 0) {
755                 /*
756                  * A relookup is required before the create to properly
757                  * position the parent chain.
758                  */
759                 tmp = hammer2_chain_lookup(&parent, &key_next,
760                                            xop->lhc, xop->lhc,
761                                            &error, 0);
762                 KKASSERT(tmp == NULL);
763                 error = hammer2_chain_create(&parent, &chain, NULL, pmp,
764                                              HAMMER2_METH_DEFAULT,
765                                              xop->lhc, 0,
766                                              HAMMER2_BREF_TYPE_INODE,
767                                              HAMMER2_INODE_BYTES,
768                                              xop->head.mtid, 0, 0);
769         }
770 done:
771         hammer2_xop_feed(&xop->head, NULL, clindex, error);
772         if (parent) {
773                 hammer2_chain_unlock(parent);
774                 hammer2_chain_drop(parent);
775         }
776         if (chain) {
777                 hammer2_chain_unlock(chain);
778                 hammer2_chain_drop(chain);
779         }
780 }
781
782 /*
783  * Directory collision resolver scan helper (backend, threaded).
784  *
785  * Used by the inode create code to locate an unused lhc.
786  */
787 void
788 hammer2_xop_scanlhc(hammer2_xop_t *arg, void *scratch, int clindex)
789 {
790         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
791         hammer2_chain_t *parent;
792         hammer2_chain_t *chain;
793         hammer2_key_t key_next;
794         int error = 0;
795
796         parent = hammer2_inode_chain(xop->head.ip1, clindex,
797                                      HAMMER2_RESOLVE_ALWAYS |
798                                      HAMMER2_RESOLVE_SHARED);
799         if (parent == NULL) {
800                 kprintf("xop_nresolve: NULL parent\n");
801                 chain = NULL;
802                 error = HAMMER2_ERROR_EIO;
803                 goto done;
804         }
805
806         /*
807          * Lookup all possibly conflicting directory entries, the feed
808          * inherits the chain's lock so do not unlock it on the iteration.
809          */
810         chain = hammer2_chain_lookup(&parent, &key_next,
811                                      xop->lhc,
812                                      xop->lhc + HAMMER2_DIRHASH_LOMASK,
813                                      &error,
814                                      HAMMER2_LOOKUP_ALWAYS |
815                                      HAMMER2_LOOKUP_SHARED);
816         while (chain) {
817                 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
818                 if (error) {
819                         hammer2_chain_unlock(chain);
820                         hammer2_chain_drop(chain);
821                         chain = NULL;   /* safety */
822                         goto done;
823                 }
824                 chain = hammer2_chain_next(&parent, chain, &key_next,
825                                            key_next,
826                                            xop->lhc + HAMMER2_DIRHASH_LOMASK,
827                                            &error,
828                                            HAMMER2_LOOKUP_ALWAYS |
829                                            HAMMER2_LOOKUP_SHARED);
830         }
831 done:
832         hammer2_xop_feed(&xop->head, NULL, clindex, error);
833         if (parent) {
834                 hammer2_chain_unlock(parent);
835                 hammer2_chain_drop(parent);
836         }
837 }
838
839 /*
840  * Generic lookup of a specific key.
841  */
842 void
843 hammer2_xop_lookup(hammer2_xop_t *arg, void *scratch, int clindex)
844 {
845         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
846         hammer2_chain_t *parent;
847         hammer2_chain_t *chain;
848         hammer2_key_t key_next;
849         int error = 0;
850
851         parent = hammer2_inode_chain(xop->head.ip1, clindex,
852                                      HAMMER2_RESOLVE_ALWAYS |
853                                      HAMMER2_RESOLVE_SHARED);
854         chain = NULL;
855         if (parent == NULL) {
856                 error = HAMMER2_ERROR_EIO;
857                 goto done;
858         }
859
860         /*
861          * Lookup all possibly conflicting directory entries, the feed
862          * inherits the chain's lock so do not unlock it on the iteration.
863          */
864         chain = hammer2_chain_lookup(&parent, &key_next,
865                                      xop->lhc, xop->lhc,
866                                      &error,
867                                      HAMMER2_LOOKUP_ALWAYS |
868                                      HAMMER2_LOOKUP_SHARED);
869         if (error == 0) {
870                 if (chain)
871                         error = chain->error;
872                 else
873                         error = HAMMER2_ERROR_ENOENT;
874         }
875         hammer2_xop_feed(&xop->head, chain, clindex, error);
876
877 done:
878         if (chain) {
879                 hammer2_chain_unlock(chain);
880                 hammer2_chain_drop(chain);
881         }
882         if (parent) {
883                 hammer2_chain_unlock(parent);
884                 hammer2_chain_drop(parent);
885         }
886 }
887
888 void
889 hammer2_xop_delete(hammer2_xop_t *arg, void *scratch, int clindex)
890 {
891         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
892         hammer2_chain_t *parent;
893         hammer2_chain_t *chain;
894         hammer2_key_t key_next;
895         int error = 0;
896
897         parent = hammer2_inode_chain(xop->head.ip1, clindex,
898                                      HAMMER2_RESOLVE_ALWAYS);
899         chain = NULL;
900         if (parent == NULL) {
901                 error = HAMMER2_ERROR_EIO;
902                 goto done;
903         }
904
905         /*
906          * Lookup all possibly conflicting directory entries, the feed
907          * inherits the chain's lock so do not unlock it on the iteration.
908          */
909         chain = hammer2_chain_lookup(&parent, &key_next,
910                                      xop->lhc, xop->lhc,
911                                      &error,
912                                      HAMMER2_LOOKUP_NODATA);
913         if (error == 0) {
914                 if (chain)
915                         error = chain->error;
916                 else
917                         error = HAMMER2_ERROR_ENOENT;
918         }
919         if (chain) {
920                 error = hammer2_chain_delete(parent, chain, xop->head.mtid,
921                                              HAMMER2_DELETE_PERMANENT);
922         }
923         hammer2_xop_feed(&xop->head, NULL, clindex, error);
924
925 done:
926         if (chain) {
927                 hammer2_chain_unlock(chain);
928                 hammer2_chain_drop(chain);
929         }
930         if (parent) {
931                 hammer2_chain_unlock(parent);
932                 hammer2_chain_drop(parent);
933         }
934 }
935
936 /*
937  * Generic scan
938  *
939  * WARNING! Fed chains must be locked shared so ownership can be transfered
940  *          and to prevent frontend/backend stalls that would occur with an
941  *          exclusive lock.  The shared lock also allows chain->data to be
942  *          retained.
943  */
944 void
945 hammer2_xop_scanall(hammer2_xop_t *arg, void *scratch, int clindex)
946 {
947         hammer2_xop_scanall_t *xop = &arg->xop_scanall;
948         hammer2_chain_t *parent;
949         hammer2_chain_t *chain;
950         hammer2_key_t key_next;
951         int error = 0;
952
953         /*
954          * Assert required flags.
955          */
956         KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED);
957         KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED);
958
959         /*
960          * The inode's chain is the iterator.  If we cannot acquire it our
961          * contribution ends here.
962          */
963         parent = hammer2_inode_chain(xop->head.ip1, clindex,
964                                      xop->resolve_flags);
965         if (parent == NULL) {
966                 kprintf("xop_readdir: NULL parent\n");
967                 goto done;
968         }
969
970         /*
971          * Generic scan of exact records.  Note that indirect blocks are
972          * automatically recursed and will not be returned.
973          */
974         chain = hammer2_chain_lookup(&parent, &key_next,
975                                      xop->key_beg, xop->key_end,
976                                      &error, xop->lookup_flags);
977         while (chain) {
978                 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
979                 if (error)
980                         goto break2;
981                 chain = hammer2_chain_next(&parent, chain, &key_next,
982                                            key_next, xop->key_end,
983                                            &error, xop->lookup_flags);
984         }
985 break2:
986         if (chain) {
987                 hammer2_chain_unlock(chain);
988                 hammer2_chain_drop(chain);
989         }
990         hammer2_chain_unlock(parent);
991         hammer2_chain_drop(parent);
992 done:
993         hammer2_xop_feed(&xop->head, NULL, clindex, error);
994 }
995
996 /************************************************************************
997  *                          INODE LAYER XOPS                            *
998  ************************************************************************
999  *
1000  */
1001 /*
1002  * Helper to create a directory entry.
1003  */
1004 void
1005 hammer2_xop_inode_mkdirent(hammer2_xop_t *arg, void *scratch, int clindex)
1006 {
1007         hammer2_xop_mkdirent_t *xop = &arg->xop_mkdirent;
1008         hammer2_chain_t *parent;
1009         hammer2_chain_t *chain;
1010         hammer2_key_t key_next;
1011         size_t data_len;
1012         int error;
1013
1014         if (hammer2_debug & 0x0001)
1015                 kprintf("dirent_create lhc %016jx clindex %d\n",
1016                         xop->lhc, clindex);
1017
1018         parent = hammer2_inode_chain(xop->head.ip1, clindex,
1019                                      HAMMER2_RESOLVE_ALWAYS);
1020         if (parent == NULL) {
1021                 error = HAMMER2_ERROR_EIO;
1022                 chain = NULL;
1023                 goto fail;
1024         }
1025         chain = hammer2_chain_lookup(&parent, &key_next,
1026                                      xop->lhc, xop->lhc,
1027                                      &error, 0);
1028         if (chain) {
1029                 error = HAMMER2_ERROR_EEXIST;
1030                 goto fail;
1031         }
1032
1033         /*
1034          * We may be able to embed the directory entry directly in the
1035          * blockref.
1036          */
1037         if (xop->dirent.namlen <= sizeof(chain->bref.check.buf))
1038                 data_len = 0;
1039         else
1040                 data_len = HAMMER2_ALLOC_MIN;
1041
1042         error = hammer2_chain_create(&parent, &chain, NULL, xop->head.ip1->pmp,
1043                                      HAMMER2_METH_DEFAULT,
1044                                      xop->lhc, 0,
1045                                      HAMMER2_BREF_TYPE_DIRENT,
1046                                      data_len,
1047                                      xop->head.mtid, 0, 0);
1048         if (error == 0) {
1049                 /*
1050                  * WARNING: chain->data->buf is sized to chain->bytes,
1051                  *          do not use sizeof(chain->data->buf), which
1052                  *          will be much larger.
1053                  */
1054                 error = hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
1055                 if (error == 0) {
1056                         chain->bref.embed.dirent = xop->dirent;
1057                         if (xop->dirent.namlen <= sizeof(chain->bref.check.buf))
1058                                 bcopy(xop->head.name1, chain->bref.check.buf,
1059                                       xop->dirent.namlen);
1060                         else
1061                                 bcopy(xop->head.name1, chain->data->buf,
1062                                       xop->dirent.namlen);
1063                 }
1064         }
1065 fail:
1066         if (parent) {
1067                 hammer2_chain_unlock(parent);
1068                 hammer2_chain_drop(parent);
1069         }
1070         hammer2_xop_feed(&xop->head, chain, clindex, error);
1071         if (chain) {
1072                 hammer2_chain_unlock(chain);
1073                 hammer2_chain_drop(chain);
1074         }
1075 }
1076
1077 /*
1078  * Inode create helper (threaded, backend)
1079  *
1080  * Used by ncreate, nmknod, nsymlink, nmkdir.
1081  * Used by nlink and rename to create HARDLINK pointers.
1082  *
1083  * Frontend holds the parent directory ip locked exclusively.  We
1084  * create the inode and feed the exclusively locked chain to the
1085  * frontend.
1086  */
1087 void
1088 hammer2_xop_inode_create(hammer2_xop_t *arg, void *scratch, int clindex)
1089 {
1090         hammer2_xop_create_t *xop = &arg->xop_create;
1091         hammer2_chain_t *parent;
1092         hammer2_chain_t *chain;
1093         hammer2_key_t key_next;
1094         int error;
1095
1096         if (hammer2_debug & 0x0001)
1097                 kprintf("inode_create lhc %016jx clindex %d\n",
1098                         xop->lhc, clindex);
1099
1100         parent = hammer2_inode_chain(xop->head.ip1, clindex,
1101                                      HAMMER2_RESOLVE_ALWAYS);
1102         if (parent == NULL) {
1103                 error = HAMMER2_ERROR_EIO;
1104                 chain = NULL;
1105                 goto fail;
1106         }
1107         chain = hammer2_chain_lookup(&parent, &key_next,
1108                                      xop->lhc, xop->lhc,
1109                                      &error, 0);
1110         if (chain) {
1111                 error = HAMMER2_ERROR_EEXIST;
1112                 goto fail;
1113         }
1114
1115         error = hammer2_chain_create(&parent, &chain, NULL, xop->head.ip1->pmp,
1116                                      HAMMER2_METH_DEFAULT,
1117                                      xop->lhc, 0,
1118                                      HAMMER2_BREF_TYPE_INODE,
1119                                      HAMMER2_INODE_BYTES,
1120                                      xop->head.mtid, 0, xop->flags);
1121         if (error == 0) {
1122                 error = hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
1123                 if (error == 0) {
1124                         chain->data->ipdata.meta = xop->meta;
1125                         if (xop->head.name1) {
1126                                 bcopy(xop->head.name1,
1127                                       chain->data->ipdata.filename,
1128                                       xop->head.name1_len);
1129                                 chain->data->ipdata.meta.name_len =
1130                                         xop->head.name1_len;
1131                         }
1132                         chain->data->ipdata.meta.name_key = xop->lhc;
1133                 }
1134         }
1135 fail:
1136         if (parent) {
1137                 hammer2_chain_unlock(parent);
1138                 hammer2_chain_drop(parent);
1139         }
1140         hammer2_xop_feed(&xop->head, chain, clindex, error);
1141         if (chain) {
1142                 hammer2_chain_unlock(chain);
1143                 hammer2_chain_drop(chain);
1144         }
1145 }
1146
1147 /*
1148  * Create inode as above but leave it detached from the hierarchy.
1149  */
1150 void
1151 hammer2_xop_inode_create_det(hammer2_xop_t *arg, void *scratch, int clindex)
1152 {
1153         hammer2_xop_create_t *xop = &arg->xop_create;
1154         hammer2_chain_t *parent;
1155         hammer2_chain_t *chain;
1156         hammer2_chain_t *null_parent;
1157         hammer2_key_t key_next;
1158         hammer2_inode_t *pip;
1159         hammer2_inode_t *iroot;
1160         int error;
1161
1162         if (hammer2_debug & 0x0001)
1163                 kprintf("inode_create_det lhc %016jx clindex %d\n",
1164                         xop->lhc, clindex);
1165
1166         pip = xop->head.ip1;
1167         iroot = pip->pmp->iroot;
1168
1169         parent = hammer2_inode_chain(iroot, clindex, HAMMER2_RESOLVE_ALWAYS);
1170
1171         if (parent == NULL) {
1172                 error = HAMMER2_ERROR_EIO;
1173                 chain = NULL;
1174                 goto fail;
1175         }
1176         chain = hammer2_chain_lookup(&parent, &key_next,
1177                                      xop->lhc, xop->lhc,
1178                                      &error, 0);
1179         if (chain) {
1180                 error = HAMMER2_ERROR_EEXIST;
1181                 goto fail;
1182         }
1183
1184         /*
1185          * Create as a detached chain with no parent.  We must specify
1186          * methods
1187          */
1188         null_parent = NULL;
1189         error = hammer2_chain_create(&null_parent, &chain,
1190                                      parent->hmp, pip->pmp,
1191                                      HAMMER2_ENC_COMP(pip->meta.comp_algo) +
1192                                      HAMMER2_ENC_CHECK(pip->meta.check_algo),
1193                                      xop->lhc, 0,
1194                                      HAMMER2_BREF_TYPE_INODE,
1195                                      HAMMER2_INODE_BYTES,
1196                                      xop->head.mtid, 0, xop->flags);
1197         if (error == 0) {
1198                 error = hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
1199                 if (error == 0) {
1200                         chain->data->ipdata.meta = xop->meta;
1201                         if (xop->head.name1) {
1202                                 bcopy(xop->head.name1,
1203                                       chain->data->ipdata.filename,
1204                                       xop->head.name1_len);
1205                                 chain->data->ipdata.meta.name_len =
1206                                         xop->head.name1_len;
1207                         }
1208                         chain->data->ipdata.meta.name_key = xop->lhc;
1209                 }
1210         }
1211 fail:
1212         if (parent) {
1213                 hammer2_chain_unlock(parent);
1214                 hammer2_chain_drop(parent);
1215         }
1216         hammer2_xop_feed(&xop->head, chain, clindex, error);
1217         if (chain) {
1218                 hammer2_chain_unlock(chain);
1219                 hammer2_chain_drop(chain);
1220         }
1221 }
1222
1223 /*
1224  * Take a detached chain and insert it into the topology
1225  */
1226 void
1227 hammer2_xop_inode_create_ins(hammer2_xop_t *arg, void *scratch, int clindex)
1228 {
1229         hammer2_xop_create_t *xop = &arg->xop_create;
1230         hammer2_chain_t *parent;
1231         hammer2_chain_t *chain;
1232         hammer2_key_t key_next;
1233         int error;
1234
1235         if (hammer2_debug & 0x0001)
1236                 kprintf("inode_create_ins lhc %016jx clindex %d\n",
1237                         xop->lhc, clindex);
1238
1239         /*
1240          * (parent) will be the insertion point for inode under iroot
1241          */
1242         parent = hammer2_inode_chain(xop->head.ip1->pmp->iroot, clindex,
1243                                      HAMMER2_RESOLVE_ALWAYS);
1244         if (parent == NULL) {
1245                 error = HAMMER2_ERROR_EIO;
1246                 chain = NULL;
1247                 goto fail;
1248         }
1249         chain = hammer2_chain_lookup(&parent, &key_next,
1250                                      xop->lhc, xop->lhc,
1251                                      &error, 0);
1252         if (chain) {
1253                 error = HAMMER2_ERROR_EEXIST;
1254                 goto fail;
1255         }
1256
1257         /*
1258          * (chain) is the detached inode that is being inserted
1259          */
1260         chain = hammer2_inode_chain(xop->head.ip1, clindex,
1261                                      HAMMER2_RESOLVE_ALWAYS);
1262         if (chain == NULL) {
1263                 error = HAMMER2_ERROR_EIO;
1264                 chain = NULL;
1265                 goto fail;
1266         }
1267
1268         /*
1269          * This create call will insert the non-NULL chain into parent.
1270          * Most of the auxillary fields are ignored since the chain already
1271          * exists.
1272          */
1273         error = hammer2_chain_create(&parent, &chain, NULL, xop->head.ip1->pmp,
1274                                      HAMMER2_METH_DEFAULT,
1275                                      xop->lhc, 0,
1276                                      HAMMER2_BREF_TYPE_INODE,
1277                                      HAMMER2_INODE_BYTES,
1278                                      xop->head.mtid, 0, xop->flags);
1279 #if 0
1280         if (error == 0) {
1281                 error = hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
1282                 if (error == 0) {
1283                         chain->data->ipdata.meta = xop->meta;
1284                         if (xop->head.name1) {
1285                                 bcopy(xop->head.name1,
1286                                       chain->data->ipdata.filename,
1287                                       xop->head.name1_len);
1288                                 chain->data->ipdata.meta.name_len =
1289                                         xop->head.name1_len;
1290                         }
1291                         chain->data->ipdata.meta.name_key = xop->lhc;
1292                 }
1293         }
1294 #endif
1295 fail:
1296         if (parent) {
1297                 hammer2_chain_unlock(parent);
1298                 hammer2_chain_drop(parent);
1299         }
1300         hammer2_xop_feed(&xop->head, chain, clindex, error);
1301         if (chain) {
1302                 hammer2_chain_unlock(chain);
1303                 hammer2_chain_drop(chain);
1304         }
1305 }
1306
1307 /*
1308  * Inode delete helper (backend, threaded)
1309  *
1310  * Generally used by hammer2_run_sideq()
1311  */
1312 void
1313 hammer2_xop_inode_destroy(hammer2_xop_t *arg, void *scratch, int clindex)
1314 {
1315         hammer2_xop_destroy_t *xop = &arg->xop_destroy;
1316         hammer2_pfs_t *pmp;
1317         hammer2_chain_t *parent;
1318         hammer2_chain_t *chain;
1319         hammer2_inode_t *ip;
1320         int error;
1321
1322         /*
1323          * We need the precise parent chain to issue the deletion.
1324          */
1325         ip = xop->head.ip1;
1326         pmp = ip->pmp;
1327
1328         chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
1329         if (chain == NULL) {
1330                 parent = NULL;
1331                 error = HAMMER2_ERROR_EIO;
1332                 goto done;
1333         }
1334
1335         if (ip->flags & HAMMER2_INODE_CREATING) {
1336                 /*
1337                  * Inode's chains are not linked into the media topology
1338                  * because it is a new inode (which is now being destroyed).
1339                  */
1340                 parent = NULL;
1341         } else {
1342                 /*
1343                  * Inode's chains are linked into the media topology
1344                  */
1345                 parent = hammer2_chain_getparent(chain, HAMMER2_RESOLVE_ALWAYS);
1346                 if (parent == NULL) {
1347                         error = HAMMER2_ERROR_EIO;
1348                         goto done;
1349                 }
1350         }
1351         KKASSERT(chain->parent == parent);
1352
1353         /*
1354          * We have the correct parent, we can issue the deletion.
1355          */
1356         hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
1357         error = 0;
1358 done:
1359         hammer2_xop_feed(&xop->head, NULL, clindex, error);
1360         if (parent) {
1361                 hammer2_chain_unlock(parent);
1362                 hammer2_chain_drop(parent);
1363         }
1364         if (chain) {
1365                 hammer2_chain_unlock(chain);
1366                 hammer2_chain_drop(chain);
1367         }
1368 }
1369
1370 void
1371 hammer2_xop_inode_unlinkall(hammer2_xop_t *arg, void *scratch, int clindex)
1372 {
1373         hammer2_xop_unlinkall_t *xop = &arg->xop_unlinkall;
1374         hammer2_chain_t *parent;
1375         hammer2_chain_t *chain;
1376         hammer2_key_t key_next;
1377         int error;
1378
1379         /*
1380          * We need the precise parent chain to issue the deletion.
1381          */
1382         parent = hammer2_inode_chain(xop->head.ip1, clindex,
1383                                      HAMMER2_RESOLVE_ALWAYS);
1384         chain = NULL;
1385         if (parent == NULL) {
1386                 error = 0;
1387                 goto done;
1388         }
1389         chain = hammer2_chain_lookup(&parent, &key_next,
1390                                      xop->key_beg, xop->key_end,
1391                                      &error, HAMMER2_LOOKUP_ALWAYS);
1392         while (chain) {
1393                 hammer2_chain_delete(parent, chain,
1394                                      xop->head.mtid, HAMMER2_DELETE_PERMANENT);
1395                 hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
1396                 /* depend on function to unlock the shared lock */
1397                 chain = hammer2_chain_next(&parent, chain, &key_next,
1398                                            key_next, xop->key_end,
1399                                            &error,
1400                                            HAMMER2_LOOKUP_ALWAYS);
1401         }
1402 done:
1403         if (error == 0)
1404                 error = HAMMER2_ERROR_ENOENT;
1405         hammer2_xop_feed(&xop->head, NULL, clindex, error);
1406         if (parent) {
1407                 hammer2_chain_unlock(parent);
1408                 hammer2_chain_drop(parent);
1409         }
1410         if (chain) {
1411                 hammer2_chain_unlock(chain);
1412                 hammer2_chain_drop(chain);
1413         }
1414 }
1415
1416 void
1417 hammer2_xop_inode_connect(hammer2_xop_t *arg, void *scratch, int clindex)
1418 {
1419         hammer2_xop_connect_t *xop = &arg->xop_connect;
1420         hammer2_inode_data_t *wipdata;
1421         hammer2_chain_t *parent;
1422         hammer2_chain_t *chain;
1423         hammer2_pfs_t *pmp;
1424         hammer2_key_t key_dummy;
1425         int error;
1426
1427         /*
1428          * Get directory, then issue a lookup to prime the parent chain
1429          * for the create.  The lookup is expected to fail.
1430          */
1431         pmp = xop->head.ip1->pmp;
1432         parent = hammer2_inode_chain(xop->head.ip1, clindex,
1433                                      HAMMER2_RESOLVE_ALWAYS);
1434         if (parent == NULL) {
1435                 chain = NULL;
1436                 error = HAMMER2_ERROR_EIO;
1437                 goto fail;
1438         }
1439         chain = hammer2_chain_lookup(&parent, &key_dummy,
1440                                      xop->lhc, xop->lhc,
1441                                      &error, 0);
1442         if (chain) {
1443                 hammer2_chain_unlock(chain);
1444                 hammer2_chain_drop(chain);
1445                 chain = NULL;
1446                 error = HAMMER2_ERROR_EEXIST;
1447                 goto fail;
1448         }
1449         if (error)
1450                 goto fail;
1451
1452         /*
1453          * Adjust the filename in the inode, set the name key.
1454          *
1455          * NOTE: Frontend must also adjust ip2->meta on success, we can't
1456          *       do it here.
1457          */
1458         chain = hammer2_inode_chain(xop->head.ip2, clindex,
1459                                     HAMMER2_RESOLVE_ALWAYS);
1460         error = hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
1461         if (error)
1462                 goto fail;
1463
1464         wipdata = &chain->data->ipdata;
1465
1466         hammer2_inode_modify(xop->head.ip2);
1467         if (xop->head.name1) {
1468                 bzero(wipdata->filename, sizeof(wipdata->filename));
1469                 bcopy(xop->head.name1, wipdata->filename, xop->head.name1_len);
1470                 wipdata->meta.name_len = xop->head.name1_len;
1471         }
1472         wipdata->meta.name_key = xop->lhc;
1473
1474         /*
1475          * Reconnect the chain to the new parent directory
1476          */
1477         error = hammer2_chain_create(&parent, &chain, NULL, pmp,
1478                                      HAMMER2_METH_DEFAULT,
1479                                      xop->lhc, 0,
1480                                      HAMMER2_BREF_TYPE_INODE,
1481                                      HAMMER2_INODE_BYTES,
1482                                      xop->head.mtid, 0, 0);
1483
1484         /*
1485          * Feed result back.
1486          */
1487 fail:
1488         hammer2_xop_feed(&xop->head, NULL, clindex, error);
1489         if (parent) {
1490                 hammer2_chain_unlock(parent);
1491                 hammer2_chain_drop(parent);
1492         }
1493         if (chain) {
1494                 hammer2_chain_unlock(chain);
1495                 hammer2_chain_drop(chain);
1496         }
1497 }
1498
1499 /*
1500  * Synchronize the in-memory inode with the chain.  This does not flush
1501  * the chain to disk.  Instead, it makes front-end inode changes visible
1502  * in the chain topology, thus visible to the backend.  This is done in an
1503  * ad-hoc manner outside of the filesystem vfs_sync, and in a controlled
1504  * manner inside the vfs_sync.
1505  */
1506 void
1507 hammer2_xop_inode_chain_sync(hammer2_xop_t *arg, void *scratch, int clindex)
1508 {
1509         hammer2_xop_fsync_t *xop = &arg->xop_fsync;
1510         hammer2_chain_t *parent;
1511         hammer2_chain_t *chain;
1512         int error;
1513
1514         parent = hammer2_inode_chain(xop->head.ip1, clindex,
1515                                      HAMMER2_RESOLVE_ALWAYS);
1516         chain = NULL;
1517         if (parent == NULL) {
1518                 error = HAMMER2_ERROR_EIO;
1519                 goto done;
1520         }
1521         if (parent->error) {
1522                 error = parent->error;
1523                 goto done;
1524         }
1525
1526         error = 0;
1527
1528         if ((xop->ipflags & HAMMER2_INODE_RESIZED) == 0) {
1529                 /* osize must be ignored */
1530         } else if (xop->meta.size < xop->osize) {
1531                 /*
1532                  * We must delete any chains beyond the EOF.  The chain
1533                  * straddling the EOF will be pending in the bioq.
1534                  */
1535                 hammer2_key_t lbase;
1536                 hammer2_key_t key_next;
1537
1538                 lbase = (xop->meta.size + HAMMER2_PBUFMASK64) &
1539                         ~HAMMER2_PBUFMASK64;
1540                 chain = hammer2_chain_lookup(&parent, &key_next,
1541                                              lbase, HAMMER2_KEY_MAX,
1542                                              &error,
1543                                              HAMMER2_LOOKUP_NODATA |
1544                                              HAMMER2_LOOKUP_NODIRECT);
1545                 while (chain) {
1546                         /*
1547                          * Degenerate embedded case, nothing to loop on
1548                          */
1549                         switch (chain->bref.type) {
1550                         case HAMMER2_BREF_TYPE_DIRENT:
1551                         case HAMMER2_BREF_TYPE_INODE:
1552                                 KKASSERT(0);
1553                                 break;
1554                         case HAMMER2_BREF_TYPE_DATA:
1555                                 hammer2_chain_delete(parent, chain,
1556                                                      xop->head.mtid,
1557                                                      HAMMER2_DELETE_PERMANENT);
1558                                 break;
1559                         }
1560                         chain = hammer2_chain_next(&parent, chain, &key_next,
1561                                                    key_next, HAMMER2_KEY_MAX,
1562                                                    &error,
1563                                                    HAMMER2_LOOKUP_NODATA |
1564                                                    HAMMER2_LOOKUP_NODIRECT);
1565                 }
1566
1567                 /*
1568                  * Reset to point at inode for following code, if necessary.
1569                  */
1570                 if (parent->bref.type != HAMMER2_BREF_TYPE_INODE) {
1571                         hammer2_chain_unlock(parent);
1572                         hammer2_chain_drop(parent);
1573                         parent = hammer2_inode_chain(xop->head.ip1,
1574                                                      clindex,
1575                                                      HAMMER2_RESOLVE_ALWAYS);
1576                         kprintf("hammer2: TRUNCATE RESET on '%s'\n",
1577                                 parent->data->ipdata.filename);
1578                 }
1579         }
1580
1581         /*
1582          * Sync the inode meta-data, potentially clear the blockset area
1583          * of direct data so it can be used for blockrefs.
1584          */
1585         if (error == 0) {
1586                 error = hammer2_chain_modify(parent, xop->head.mtid, 0, 0);
1587                 if (error == 0) {
1588                         parent->data->ipdata.meta = xop->meta;
1589                         if (xop->clear_directdata) {
1590                                 bzero(&parent->data->ipdata.u.blockset,
1591                                       sizeof(parent->data->ipdata.u.blockset));
1592                         }
1593                 }
1594         }
1595 done:
1596         if (chain) {
1597                 hammer2_chain_unlock(chain);
1598                 hammer2_chain_drop(chain);
1599         }
1600         if (parent) {
1601                 hammer2_chain_unlock(parent);
1602                 hammer2_chain_drop(parent);
1603         }
1604         hammer2_xop_feed(&xop->head, NULL, clindex, error);
1605 }