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