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