hammer2 - Stabilization for cluster code, features
[dragonfly.git] / sys / vfs / hammer2 / hammer2_xops.c
1 /*
2  * Copyright (c) 2011-2015 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7  * by Daniel Flores (GSOC 2013 - mentored by Matthew Dillon, compression) 
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  * 3. Neither the name of The DragonFly Project nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific, prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 /*
37  * Per-node backend for kernel filesystem interface.
38  *
39  * This executes a VOP concurrently on multiple nodes, each node via its own
40  * thread, and competes to advance the original request.  The original
41  * request is retired the moment all requirements are met, even if the
42  * operation is still in-progress on some nodes.
43  */
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/fcntl.h>
48 #include <sys/buf.h>
49 #include <sys/proc.h>
50 #include <sys/namei.h>
51 #include <sys/mount.h>
52 #include <sys/vnode.h>
53 #include <sys/mountctl.h>
54 #include <sys/dirent.h>
55 #include <sys/uio.h>
56 #include <sys/objcache.h>
57 #include <sys/event.h>
58 #include <sys/file.h>
59 #include <vfs/fifofs/fifo.h>
60
61 #include "hammer2.h"
62
63 /*
64  * Determine if the specified directory is empty.  Returns 0 on success.
65  *
66  * May return 0, ENOTDIR, or EAGAIN.
67  */
68 static
69 int
70 checkdirempty(hammer2_chain_t *oparent, hammer2_chain_t *ochain, int clindex)
71 {
72         hammer2_chain_t *parent;
73         hammer2_chain_t *chain;
74         hammer2_key_t key_next;
75         int cache_index = -1;
76         int error;
77
78         error = 0;
79         chain = hammer2_chain_lookup_init(ochain, 0);
80
81         if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
82                 if (oparent)
83                         hammer2_chain_unlock(oparent);
84
85                 parent = NULL;
86                 error = hammer2_chain_hardlink_find(&parent, &chain,
87                                                     clindex, 0);
88                 if (parent) {
89                         hammer2_chain_unlock(parent);
90                         hammer2_chain_drop(parent);
91                 }
92                 if (oparent) {
93                         hammer2_chain_lock(oparent, HAMMER2_RESOLVE_ALWAYS);
94                         if (ochain->parent != oparent) {
95                                 if (chain) {
96                                         hammer2_chain_unlock(chain);
97                                         hammer2_chain_drop(chain);
98                                 }
99                                 kprintf("H2EAGAIN\n");
100
101                                 return EAGAIN;
102                         }
103                 }
104         }
105
106         parent = chain;
107         chain = NULL;
108         if (parent) {
109                 chain = hammer2_chain_lookup(&parent, &key_next,
110                                              HAMMER2_DIRHASH_VISIBLE,
111                                              HAMMER2_KEY_MAX,
112                                              &cache_index, 0);
113         }
114         if (chain) {
115                 error = ENOTEMPTY;
116                 hammer2_chain_unlock(chain);
117                 hammer2_chain_drop(chain);
118         } else {
119                 error = 0;
120         }
121         hammer2_chain_lookup_done(parent);
122
123         return error;
124 }
125
126 /*
127  * Backend for hammer2_vfs_root()
128  *
129  * This is called when a newly mounted PFS has not yet synchronized
130  * to the inode_tid and modify_tid.
131  */
132 void
133 hammer2_xop_ipcluster(hammer2_thread_t *thr, hammer2_xop_t *arg)
134 {
135         hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster;
136         hammer2_chain_t *chain;
137         int error;
138
139         chain = hammer2_inode_chain(xop->head.ip1, thr->clindex,
140                                     HAMMER2_RESOLVE_ALWAYS |
141                                     HAMMER2_RESOLVE_SHARED);
142         if (chain)
143                 error = chain->error;
144         else
145                 error = EIO;
146                 
147         hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
148         if (chain) {
149                 hammer2_chain_unlock(chain);
150                 hammer2_chain_drop(chain);
151         }
152 }
153
154 /*
155  * Backend for hammer2_vop_readdir()
156  */
157 void
158 hammer2_xop_readdir(hammer2_thread_t *thr, hammer2_xop_t *arg)
159 {
160         hammer2_xop_readdir_t *xop = &arg->xop_readdir;
161         hammer2_chain_t *parent;
162         hammer2_chain_t *chain;
163         hammer2_key_t key_next;
164         hammer2_key_t lkey;
165         int cache_index = -1;
166         int error = 0;
167
168         lkey = xop->lkey;
169         if (hammer2_debug & 0x0020)
170                 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
171
172         /*
173          * The inode's chain is the iterator.  If we cannot acquire it our
174          * contribution ends here.
175          */
176         parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
177                                      HAMMER2_RESOLVE_ALWAYS |
178                                      HAMMER2_RESOLVE_SHARED);
179         if (parent == NULL) {
180                 kprintf("xop_readdir: NULL parent\n");
181                 goto done;
182         }
183
184         /*
185          * Directory scan [re]start and loop, the feed inherits the chain's
186          * lock so do not unlock it on the iteration.
187          */
188         chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey,
189                                      &cache_index, HAMMER2_LOOKUP_SHARED);
190         if (chain == NULL) {
191                 chain = hammer2_chain_lookup(&parent, &key_next,
192                                              lkey, HAMMER2_KEY_MAX,
193                                              &cache_index,
194                                              HAMMER2_LOOKUP_SHARED);
195         }
196         while (chain) {
197                 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0);
198                 if (error)
199                         break;
200                 chain = hammer2_chain_next(&parent, chain, &key_next,
201                                            key_next, HAMMER2_KEY_MAX,
202                                            &cache_index,
203                                            HAMMER2_LOOKUP_SHARED);
204         }
205         if (chain) {
206                 hammer2_chain_unlock(chain);
207                 hammer2_chain_drop(chain);
208         }
209         hammer2_chain_unlock(parent);
210         hammer2_chain_drop(parent);
211 done:
212         hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
213 }
214
215 /*
216  * Backend for hammer2_vop_nresolve()
217  */
218 void
219 hammer2_xop_nresolve(hammer2_thread_t *thr, hammer2_xop_t *arg)
220 {
221         hammer2_xop_nresolve_t *xop = &arg->xop_nresolve;
222         hammer2_chain_t *parent;
223         hammer2_chain_t *chain;
224         const hammer2_inode_data_t *ripdata;
225         const char *name;
226         size_t name_len;
227         hammer2_key_t key_next;
228         hammer2_key_t lhc;
229         int cache_index = -1;   /* XXX */
230         int error;
231
232         parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
233                                      HAMMER2_RESOLVE_ALWAYS |
234                                      HAMMER2_RESOLVE_SHARED);
235         if (parent == NULL) {
236                 kprintf("xop_nresolve: NULL parent\n");
237                 chain = NULL;
238                 error = EIO;
239                 goto done;
240         }
241         name = xop->head.name1;
242         name_len = xop->head.name1_len;
243
244         /*
245          * Lookup the directory entry
246          */
247         lhc = hammer2_dirhash(name, name_len);
248         chain = hammer2_chain_lookup(&parent, &key_next,
249                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
250                                      &cache_index,
251                                      HAMMER2_LOOKUP_ALWAYS |
252                                      HAMMER2_LOOKUP_SHARED);
253         while (chain) {
254                 ripdata = &chain->data->ipdata;
255                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
256                     ripdata->meta.name_len == name_len &&
257                     bcmp(ripdata->filename, name, name_len) == 0) {
258                         break;
259                 }
260                 chain = hammer2_chain_next(&parent, chain, &key_next,
261                                            key_next,
262                                            lhc + HAMMER2_DIRHASH_LOMASK,
263                                            &cache_index,
264                                            HAMMER2_LOOKUP_ALWAYS |
265                                            HAMMER2_LOOKUP_SHARED);
266         }
267
268         /*
269          * If the entry is a hardlink pointer, resolve it.
270          */
271         error = 0;
272         if (chain) {
273                 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
274                         error = hammer2_chain_hardlink_find(&parent, &chain,
275                                                             thr->clindex,
276                                                         HAMMER2_LOOKUP_SHARED);
277                 }
278         }
279 done:
280         error = hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
281         if (chain) {
282                 hammer2_chain_unlock(chain);
283                 hammer2_chain_drop(chain);
284         }
285         if (parent) {
286                 hammer2_chain_unlock(parent);
287                 hammer2_chain_drop(parent);
288         }
289 }
290
291 /*
292  * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper
293  * for hammer2_vop_nrename().
294  *
295  * This function locates and removes a directory entry.  If the entry is
296  * a hardlink pointer, this function does NOT remove the hardlink target,
297  * but will lookup and return the hardlink target.
298  *
299  * Note that any hardlink target's nlinks may not be synchronized to the
300  * in-memory inode.  hammer2_inode_unlink_finisher() is responsible for the
301  * final disposition of the hardlink target.
302  *
303  * If an inode pointer we lookup and return the actual inode.  If not, we
304  * return the deleted directory entry.
305  *
306  * The frontend is responsible for moving open inodes to the hidden directory
307  * and for decrementing nlinks.
308  */
309 void
310 hammer2_xop_unlink(hammer2_thread_t *thr, hammer2_xop_t *arg)
311 {
312         hammer2_xop_unlink_t *xop = &arg->xop_unlink;
313         hammer2_chain_t *parent;
314         hammer2_chain_t *chain;
315         const hammer2_inode_data_t *ripdata;
316         const char *name;
317         size_t name_len;
318         hammer2_key_t key_next;
319         hammer2_key_t lhc;
320         int cache_index = -1;   /* XXX */
321         int error;
322
323 again:
324         /*
325          * Requires exclusive lock
326          */
327         parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
328                                      HAMMER2_RESOLVE_ALWAYS);
329         chain = NULL;
330         if (parent == NULL) {
331                 kprintf("xop_nresolve: NULL parent\n");
332                 error = EIO;
333                 goto done;
334         }
335         name = xop->head.name1;
336         name_len = xop->head.name1_len;
337
338         /*
339          * Lookup the directory entry
340          */
341         lhc = hammer2_dirhash(name, name_len);
342         chain = hammer2_chain_lookup(&parent, &key_next,
343                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
344                                      &cache_index,
345                                      HAMMER2_LOOKUP_ALWAYS);
346         while (chain) {
347                 ripdata = &chain->data->ipdata;
348                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
349                     ripdata->meta.name_len == name_len &&
350                     bcmp(ripdata->filename, name, name_len) == 0) {
351                         break;
352                 }
353                 chain = hammer2_chain_next(&parent, chain, &key_next,
354                                            key_next,
355                                            lhc + HAMMER2_DIRHASH_LOMASK,
356                                            &cache_index,
357                                            HAMMER2_LOOKUP_ALWAYS);
358         }
359
360         /*
361          * The directory entry will almost always be a hardlink pointer,
362          * which we permanently delete.  Otherwise we go by xop->dopermanent.
363          * Note that the target chain's nlinks may not be synchronized with
364          * the in-memory hammer2_inode_t structure, so we don't try to do
365          * anything fancy here.
366          */
367         error = 0;
368         if (chain) {
369                 int dopermanent = xop->dopermanent;
370                 uint8_t type;
371
372                 /*
373                  * If the directory entry is the actual inode then use its
374                  * type for the directory typing tests, otherwise if it is
375                  * a hardlink pointer then use the secondary type field for
376                  * directory typing tests.
377                  *
378                  * Also, hardlink pointers are always permanently deleted
379                  * (because they aren't the actual inode).
380                  */
381                 type = chain->data->ipdata.meta.type;
382                 if (type == HAMMER2_OBJTYPE_HARDLINK) {
383                         type = chain->data->ipdata.meta.target_type;
384                         dopermanent |= HAMMER2_DELETE_PERMANENT;
385                 }
386
387                 /*
388                  * Check directory typing and delete the entry.  Note that
389                  * nlinks adjustments are made on the real inode by the
390                  * frontend, not here.
391                  *
392                  * Unfortunately, checkdirempty() may have to unlock (parent).
393                  * If it no longer matches chain->parent after re-locking,
394                  * EAGAIN is returned.
395                  */
396                 if (type == HAMMER2_OBJTYPE_DIRECTORY &&
397                     (error = checkdirempty(parent, chain, thr->clindex)) != 0) {
398                         /* error may be EAGAIN or ENOTEMPTY */
399                         if (error == EAGAIN) {
400                                 hammer2_chain_unlock(chain);
401                                 hammer2_chain_drop(chain);
402                                 hammer2_chain_unlock(parent);
403                                 hammer2_chain_drop(parent);
404                                 goto again;
405                         }
406                 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
407                     xop->isdir == 0) {
408                         error = ENOTDIR;
409                 } else if (type != HAMMER2_OBJTYPE_DIRECTORY &&
410                            xop->isdir >= 1) {
411                         error = EISDIR;
412                 } else {
413                         /*
414                          * This deletes the directory entry itself, which is
415                          * also the inode when nlinks == 1.  Hardlink targets
416                          * are handled in the next conditional.
417                          */
418                         error = chain->error;
419                         hammer2_chain_delete(parent, chain,
420                                              xop->head.mtid, dopermanent);
421                 }
422         }
423
424         /*
425          * If the entry is a hardlink pointer, resolve it.  We do not try
426          * to manipulate the contents of the hardlink target as it might
427          * not be synchronized with the front-end hammer2_inode_t.  Nor do
428          * we try to lookup the front-end hammer2_inode_t here (we are the
429          * backend!).
430          */
431         if (chain &&
432             chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
433                 int error2;
434
435                 lhc = chain->data->ipdata.meta.inum;
436
437                 error2 = hammer2_chain_hardlink_find(&parent, &chain,
438                                                      thr->clindex, 0);
439                 if (error2) {
440                         kprintf("hardlink_find: %016jx %p failed\n",
441                                 lhc, chain);
442                         error2 = 0;     /* silently ignore */
443                 }
444                 if (error == 0)
445                         error = error2;
446         }
447
448         /*
449          * Return the inode target for further action.  Typically used by
450          * hammer2_inode_unlink_finisher().
451          */
452 done:
453         hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
454         if (chain) {
455                 hammer2_chain_unlock(chain);
456                 hammer2_chain_drop(chain);
457                 chain = NULL;
458         }
459         if (parent) {
460                 hammer2_chain_unlock(parent);
461                 hammer2_chain_drop(parent);
462                 parent = NULL;
463         }
464 }
465
466 #if 0
467 /*
468  * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
469  *
470  * ip1 - fdip
471  * ip2 - ip
472  * ip3 - cdip
473  *
474  * If a hardlink pointer:
475  *      The existing hardlink target {fdip,ip} must be moved to another
476  *      directory {cdip,ip}
477  *
478  * If not a hardlink pointer:
479  *      Convert the target {fdip,ip} to a hardlink target {cdip,ip} and
480  *      replace the original namespace {fdip,name} with a hardlink pointer.
481  */
482 void
483 hammer2_xop_nlink(hammer2_thread_t *thr, hammer2_xop_t *arg)
484 {
485         hammer2_xop_nlink_t *xop = &arg->xop_nlink;
486         hammer2_pfs_t *pmp;
487         hammer2_inode_data_t *wipdata;
488         hammer2_chain_t *parent;
489         hammer2_chain_t *chain;
490         hammer2_chain_t *tmp;
491         hammer2_inode_t *ip;
492         hammer2_key_t key_dummy;
493         int cache_index = -1;
494         int error;
495         int did_delete = 0;
496
497         /*
498          * We need the precise parent chain to issue the deletion.
499          */
500         ip = xop->head.ip2;
501         pmp = ip->pmp;
502         parent = hammer2_inode_chain(ip, thr->clindex, HAMMER2_RESOLVE_ALWAYS);
503         if (parent)
504                 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
505         if (parent == NULL) {
506                 chain = NULL;
507                 error = EIO;
508                 goto done;
509         }
510         chain = hammer2_inode_chain(ip, thr->clindex, HAMMER2_RESOLVE_ALWAYS);
511         if (chain == NULL) {
512                 error = EIO;
513                 goto done;
514         }
515         KKASSERT(chain->parent == parent);
516
517         if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
518                 /*
519                  * Delete the original chain and hold onto it for the move
520                  * to cdir.
521                  *
522                  * Replace the namespace with a hardlink pointer if the
523                  * chain being moved is not already a hardlink target.
524                  */
525                 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
526                 did_delete = 1;
527
528                 tmp = NULL;
529                 error = hammer2_chain_create(&parent, &tmp,
530                                              pmp, HAMMER2_METH_DEFAULT,
531                                              chain->bref.key, 0,
532                                              HAMMER2_BREF_TYPE_INODE,
533                                              HAMMER2_INODE_BYTES,
534                                              xop->head.mtid, 0, 0);
535                 if (error)
536                         goto done;
537                 hammer2_chain_modify(tmp, xop->head.mtid, 0, 0);
538                 wipdata = &tmp->data->ipdata;
539                 bzero(wipdata, sizeof(*wipdata));
540                 wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
541                 wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
542                 bcopy(chain->data->ipdata.filename, wipdata->filename,
543                       chain->data->ipdata.meta.name_len);
544                 wipdata->meta.target_type = chain->data->ipdata.meta.type;
545                 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
546                 wipdata->meta.inum = ip->meta.inum;
547                 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
548                 wipdata->meta.nlinks = 1;
549                 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
550
551                 hammer2_chain_unlock(tmp);
552                 hammer2_chain_drop(tmp);
553         } else if (xop->head.ip1 != xop->head.ip3) {
554                 /*
555                  * Delete the hardlink target so it can be moved
556                  * to cdir.
557                  */
558                 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
559                 did_delete = 1;
560         } else {
561                 /*
562                  * Deletion not necessary (just a nlinks update).
563                  */
564                 did_delete = 0;
565         }
566
567         hammer2_chain_unlock(parent);
568         hammer2_chain_drop(parent);
569         parent = NULL;
570
571         /*
572          * Ok, back to the deleted chain.  We must reconnect this chain
573          * as a hardlink target to cdir (ip3).
574          *
575          * WARNING! Frontend assumes filename length is 18 bytes.
576          */
577         if (did_delete) {
578                 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
579                 wipdata = &chain->data->ipdata;
580                 ksnprintf(wipdata->filename, sizeof(wipdata->filename),
581                           "0x%016jx", (intmax_t)ip->meta.inum);
582                 wipdata->meta.name_len = strlen(wipdata->filename);
583                 wipdata->meta.name_key = ip->meta.inum;
584
585                 /*
586                  * We must seek parent properly for the create to reattach
587                  * chain.  XXX just use chain->parent or
588                  * inode_chain_and_parent() ?
589                  */
590                 parent = hammer2_inode_chain(xop->head.ip3, thr->clindex,
591                                              HAMMER2_RESOLVE_ALWAYS);
592                 if (parent == NULL) {
593                         error = EIO;
594                         goto done;
595                 }
596                 tmp = hammer2_chain_lookup(&parent, &key_dummy,
597                                            ip->meta.inum, ip->meta.inum,
598                                            &cache_index, 0);
599                 if (tmp) {
600                         hammer2_chain_unlock(tmp);
601                         hammer2_chain_drop(tmp);
602                         error = EEXIST;
603                         goto done;
604                 }
605                 error = hammer2_chain_create(&parent, &chain,
606                                              pmp, HAMMER2_METH_DEFAULT,
607                                              wipdata->meta.name_key, 0,
608                                              HAMMER2_BREF_TYPE_INODE,
609                                              HAMMER2_INODE_BYTES,
610                                              xop->head.mtid, 0, 0);
611         } else {
612                 error = 0;
613         }
614
615         /*
616          * Bump nlinks to synchronize with frontend.
617          */
618         if (xop->nlinks_delta) {
619                 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
620                 chain->data->ipdata.meta.nlinks += xop->nlinks_delta;
621         }
622
623         /*
624          * To avoid having to scan the collision space we can simply
625          * reuse the inode's original name_key.  But ip->meta.name_key
626          * may have already been updated by the front-end, so use xop->lhc.
627          */
628 done:
629         hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
630         if (parent) {
631                 hammer2_chain_unlock(parent);
632                 hammer2_chain_drop(parent);
633         }
634         if (chain) {
635                 hammer2_chain_unlock(chain);
636                 hammer2_chain_drop(chain);
637         }
638 }
639 #endif
640
641 /*
642  * Backend for hammer2_vop_nrename()
643  *
644  * This handles the final step of renaming, either renaming the
645  * actual inode or renaming the hardlink pointer.
646  */
647 void
648 hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *arg)
649 {
650         hammer2_xop_nrename_t *xop = &arg->xop_nrename;
651         hammer2_pfs_t *pmp;
652         hammer2_chain_t *parent;
653         hammer2_chain_t *chain;
654         hammer2_chain_t *tmp;
655         hammer2_inode_t *ip;
656         hammer2_key_t key_dummy;
657         int cache_index = -1;
658         int error;
659
660         /*
661          * We need the precise parent chain to issue the deletion.
662          *
663          * If this is not a hardlink target we can act on the inode,
664          * otherwise we have to locate the hardlink pointer.
665          */
666         ip = xop->head.ip2;
667         pmp = ip->pmp;
668         chain = NULL;
669
670         if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
671                 /*
672                  * Find ip's direct parent chain.
673                  */
674                 parent = hammer2_inode_chain(ip, thr->clindex,
675                                              HAMMER2_RESOLVE_ALWAYS);
676                 if (parent)
677                         hammer2_chain_getparent(&parent,
678                                                 HAMMER2_RESOLVE_ALWAYS);
679                 if (parent == NULL) {
680                         error = EIO;
681                         goto done;
682                 }
683                 chain = hammer2_inode_chain(ip, thr->clindex,
684                                             HAMMER2_RESOLVE_ALWAYS);
685                 if (chain == NULL) {
686                         error = EIO;
687                         goto done;
688                 }
689         } else {
690                 /*
691                  * The hardlink pointer for the head.ip1 hardlink target
692                  * is in fdip, do a namespace search.
693                  */
694                 const hammer2_inode_data_t *ripdata;
695                 hammer2_key_t lhc;
696                 hammer2_key_t key_next;
697                 const char *name;
698                 size_t name_len;
699
700                 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
701                                              HAMMER2_RESOLVE_ALWAYS);
702                 if (parent == NULL) {
703                         kprintf("xop_nrename: NULL parent\n");
704                         error = EIO;
705                         goto done;
706                 }
707                 name = xop->head.name1;
708                 name_len = xop->head.name1_len;
709
710                 /*
711                  * Lookup the directory entry
712                  */
713                 lhc = hammer2_dirhash(name, name_len);
714                 chain = hammer2_chain_lookup(&parent, &key_next,
715                                              lhc, lhc + HAMMER2_DIRHASH_LOMASK,
716                                              &cache_index,
717                                              HAMMER2_LOOKUP_ALWAYS);
718                 while (chain) {
719                         ripdata = &chain->data->ipdata;
720                         if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
721                             ripdata->meta.name_len == name_len &&
722                             bcmp(ripdata->filename, name, name_len) == 0) {
723                                 break;
724                         }
725                         chain = hammer2_chain_next(&parent, chain, &key_next,
726                                                    key_next,
727                                                    lhc + HAMMER2_DIRHASH_LOMASK,
728                                                    &cache_index,
729                                                    HAMMER2_LOOKUP_ALWAYS);
730                 }
731         }
732
733         if (chain == NULL) {
734                 /* XXX shouldn't happen, but does under fsstress */
735                 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\"  ENOENT\n",
736                         xop->head.name1,
737                         xop->head.name2);
738                 error = ENOENT;
739                 goto done;
740         }
741
742         /*
743          * Delete it, then create it in the new namespace.
744          */
745         hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
746         hammer2_chain_unlock(parent);
747         hammer2_chain_drop(parent);
748         parent = NULL;          /* safety */
749
750         /*
751          * Ok, back to the deleted chain.  We must reconnect this chain
752          * to tdir (ip3).  The chain (a real inode or a hardlink pointer)
753          * is not otherwise modified.
754          *
755          * Frontend is expected to replicate the same inode meta data
756          * modifications.
757          *
758          * NOTE!  This chain may not represent the actual inode, it
759          *        can be a hardlink pointer.
760          *
761          * XXX in-inode parent directory specification?
762          */
763         if (chain->data->ipdata.meta.name_key != xop->lhc ||
764             xop->head.name1_len != xop->head.name2_len ||
765             bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
766                 hammer2_inode_data_t *wipdata;
767
768                 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
769                 wipdata = &chain->data->ipdata;
770
771                 bzero(wipdata->filename, sizeof(wipdata->filename));
772                 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
773                 wipdata->meta.name_key = xop->lhc;
774                 wipdata->meta.name_len = xop->head.name2_len;
775         }
776         if (chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) {
777                 hammer2_inode_data_t *wipdata;
778
779                 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
780                 wipdata = &chain->data->ipdata;
781
782                 wipdata->meta.iparent = xop->head.ip3->meta.inum;
783         }
784
785         /*
786          * We must seek parent properly for the create.
787          */
788         parent = hammer2_inode_chain(xop->head.ip3, thr->clindex,
789                                      HAMMER2_RESOLVE_ALWAYS);
790         if (parent == NULL) {
791                 error = EIO;
792                 goto done;
793         }
794         tmp = hammer2_chain_lookup(&parent, &key_dummy,
795                                    xop->lhc, xop->lhc,
796                                    &cache_index, 0);
797         if (tmp) {
798                 hammer2_chain_unlock(tmp);
799                 hammer2_chain_drop(tmp);
800                 error = EEXIST;
801                 goto done;
802         }
803
804         error = hammer2_chain_create(&parent, &chain,
805                                      pmp, HAMMER2_METH_DEFAULT,
806                                      xop->lhc, 0,
807                                      HAMMER2_BREF_TYPE_INODE,
808                                      HAMMER2_INODE_BYTES,
809                                      xop->head.mtid, 0, 0);
810 done:
811         hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
812         if (parent) {
813                 hammer2_chain_unlock(parent);
814                 hammer2_chain_drop(parent);
815         }
816         if (chain) {
817                 hammer2_chain_unlock(chain);
818                 hammer2_chain_drop(chain);
819         }
820 }
821
822 /*
823  * Directory collision resolver scan helper (backend, threaded).
824  *
825  * Used by the inode create code to locate an unused lhc.
826  */
827 void
828 hammer2_xop_scanlhc(hammer2_thread_t *thr, hammer2_xop_t *arg)
829 {
830         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
831         hammer2_chain_t *parent;
832         hammer2_chain_t *chain;
833         hammer2_key_t key_next;
834         int cache_index = -1;   /* XXX */
835         int error = 0;
836
837         parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
838                                      HAMMER2_RESOLVE_ALWAYS |
839                                      HAMMER2_RESOLVE_SHARED);
840         if (parent == NULL) {
841                 kprintf("xop_nresolve: NULL parent\n");
842                 chain = NULL;
843                 error = EIO;
844                 goto done;
845         }
846
847         /*
848          * Lookup all possibly conflicting directory entries, the feed
849          * inherits the chain's lock so do not unlock it on the iteration.
850          */
851         chain = hammer2_chain_lookup(&parent, &key_next,
852                                      xop->lhc,
853                                      xop->lhc + HAMMER2_DIRHASH_LOMASK,
854                                      &cache_index,
855                                      HAMMER2_LOOKUP_ALWAYS |
856                                      HAMMER2_LOOKUP_SHARED);
857         while (chain) {
858                 error = hammer2_xop_feed(&xop->head, chain, thr->clindex,
859                                          chain->error);
860                 if (error) {
861                         hammer2_chain_unlock(chain);
862                         hammer2_chain_drop(chain);
863                         chain = NULL;   /* safety */
864                         break;
865                 }
866                 chain = hammer2_chain_next(&parent, chain, &key_next,
867                                            key_next,
868                                            xop->lhc + HAMMER2_DIRHASH_LOMASK,
869                                            &cache_index,
870                                            HAMMER2_LOOKUP_ALWAYS |
871                                            HAMMER2_LOOKUP_SHARED);
872         }
873 done:
874         hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
875         if (parent) {
876                 hammer2_chain_unlock(parent);
877                 hammer2_chain_drop(parent);
878         }
879 }
880
881 /*
882  * Generic lookup of a specific key.
883  *
884  * Used by the inode hidden directory code to find the hidden directory.
885  */
886 void
887 hammer2_xop_lookup(hammer2_thread_t *thr, hammer2_xop_t *arg)
888 {
889         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
890         hammer2_chain_t *parent;
891         hammer2_chain_t *chain;
892         hammer2_key_t key_next;
893         int cache_index = -1;   /* XXX */
894         int error = 0;
895
896         parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
897                                      HAMMER2_RESOLVE_ALWAYS |
898                                      HAMMER2_RESOLVE_SHARED);
899         chain = NULL;
900         if (parent == NULL) {
901                 error = EIO;
902                 goto done;
903         }
904
905         /*
906          * Lookup all possibly conflicting directory entries, the feed
907          * inherits the chain's lock so do not unlock it on the iteration.
908          */
909         chain = hammer2_chain_lookup(&parent, &key_next,
910                                      xop->lhc, xop->lhc,
911                                      &cache_index,
912                                      HAMMER2_LOOKUP_ALWAYS |
913                                      HAMMER2_LOOKUP_SHARED);
914         if (chain)
915                 hammer2_xop_feed(&xop->head, chain, thr->clindex, chain->error);
916         else
917                 hammer2_xop_feed(&xop->head, NULL, thr->clindex, ENOENT);
918
919 done:
920         if (chain) {
921                 hammer2_chain_unlock(chain);
922                 hammer2_chain_drop(chain);
923         }
924         if (parent) {
925                 hammer2_chain_unlock(parent);
926                 hammer2_chain_drop(parent);
927         }
928 }
929
930 /*
931  * Generic scan
932  *
933  * WARNING! Fed chains must be locked shared so ownership can be transfered
934  *          and to prevent frontend/backend stalls that would occur with an
935  *          exclusive lock.  The shared lock also allows chain->data to be
936  *          retained.
937  */
938 void
939 hammer2_xop_scanall(hammer2_thread_t *thr, hammer2_xop_t *arg)
940 {
941         hammer2_xop_scanall_t *xop = &arg->xop_scanall;
942         hammer2_chain_t *parent;
943         hammer2_chain_t *chain;
944         hammer2_key_t key_next;
945         int cache_index = -1;
946         int error = 0;
947
948         /*
949          * Assert required flags.
950          */
951         KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED);
952         KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED);
953
954         /*
955          * The inode's chain is the iterator.  If we cannot acquire it our
956          * contribution ends here.
957          */
958         parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
959                                      xop->resolve_flags);
960         if (parent == NULL) {
961                 kprintf("xop_readdir: NULL parent\n");
962                 goto done;
963         }
964
965         /*
966          * Generic scan of exact records.  Note that indirect blocks are
967          * automatically recursed and will not be returned.
968          */
969         chain = hammer2_chain_lookup(&parent, &key_next,
970                                      xop->key_beg, xop->key_end,
971                                      &cache_index, xop->lookup_flags);
972         while (chain) {
973                 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0);
974                 if (error)
975                         break;
976                 chain = hammer2_chain_next(&parent, chain, &key_next,
977                                            key_next, xop->key_end,
978                                            &cache_index, xop->lookup_flags);
979         }
980         if (chain) {
981                 hammer2_chain_unlock(chain);
982                 hammer2_chain_drop(chain);
983         }
984         hammer2_chain_unlock(parent);
985         hammer2_chain_drop(parent);
986 done:
987         hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
988 }