hammer2 - Start work on inode indexing - MAJOR CHANGE
[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 static
67 int
68 checkdirempty(hammer2_chain_t *orig_parent)
69 {
70         hammer2_chain_t *parent;
71         hammer2_chain_t *chain;
72         hammer2_key_t key_next;
73         int cache_index = -1;
74         int error;
75
76         parent = hammer2_chain_lookup_init(orig_parent, 0);
77         chain = hammer2_chain_lookup(&parent, &key_next,
78                                      HAMMER2_DIRHASH_VISIBLE,
79                                      HAMMER2_KEY_MAX,
80                                      &cache_index, 0);
81         error = chain ? ENOTEMPTY : 0;
82         if (chain) {
83                 hammer2_chain_unlock(chain);
84                 hammer2_chain_drop(chain);
85         }
86         hammer2_chain_lookup_done(parent);
87
88         return error;
89 }
90
91 /*
92  * Backend for hammer2_vfs_root()
93  *
94  * This is called when a newly mounted PFS has not yet synchronized
95  * to the inode_tid and modify_tid.
96  */
97 void
98 hammer2_xop_ipcluster(hammer2_xop_t *arg, int clindex)
99 {
100         hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster;
101         hammer2_chain_t *chain;
102         int error;
103
104         chain = hammer2_inode_chain(xop->head.ip1, clindex,
105                                     HAMMER2_RESOLVE_ALWAYS |
106                                     HAMMER2_RESOLVE_SHARED);
107         if (chain)
108                 error = chain->error;
109         else
110                 error = EIO;
111                 
112         hammer2_xop_feed(&xop->head, chain, clindex, error);
113         if (chain) {
114                 hammer2_chain_unlock(chain);
115                 hammer2_chain_drop(chain);
116         }
117 }
118
119 /*
120  * Backend for hammer2_vop_readdir()
121  */
122 void
123 hammer2_xop_readdir(hammer2_xop_t *arg, int clindex)
124 {
125         hammer2_xop_readdir_t *xop = &arg->xop_readdir;
126         hammer2_chain_t *parent;
127         hammer2_chain_t *chain;
128         hammer2_key_t key_next;
129         hammer2_key_t lkey;
130         int cache_index = -1;
131         int error = 0;
132
133         lkey = xop->lkey;
134         if (hammer2_debug & 0x0020)
135                 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
136
137         /*
138          * The inode's chain is the iterator.  If we cannot acquire it our
139          * contribution ends here.
140          */
141         parent = hammer2_inode_chain(xop->head.ip1, clindex,
142                                      HAMMER2_RESOLVE_ALWAYS |
143                                      HAMMER2_RESOLVE_SHARED);
144         if (parent == NULL) {
145                 kprintf("xop_readdir: NULL parent\n");
146                 goto done;
147         }
148
149         /*
150          * Directory scan [re]start and loop, the feed inherits the chain's
151          * lock so do not unlock it on the iteration.
152          */
153         chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey,
154                                      &cache_index, HAMMER2_LOOKUP_SHARED);
155         if (chain == NULL) {
156                 chain = hammer2_chain_lookup(&parent, &key_next,
157                                              lkey, HAMMER2_KEY_MAX,
158                                              &cache_index,
159                                              HAMMER2_LOOKUP_SHARED);
160         }
161         while (chain) {
162                 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
163                 if (error)
164                         break;
165                 chain = hammer2_chain_next(&parent, chain, &key_next,
166                                            key_next, HAMMER2_KEY_MAX,
167                                            &cache_index,
168                                            HAMMER2_LOOKUP_SHARED);
169         }
170         if (chain) {
171                 hammer2_chain_unlock(chain);
172                 hammer2_chain_drop(chain);
173         }
174         hammer2_chain_unlock(parent);
175         hammer2_chain_drop(parent);
176 done:
177         hammer2_xop_feed(&xop->head, NULL, clindex, error);
178 }
179
180 /*
181  * Backend for hammer2_vop_nresolve()
182  */
183 void
184 hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex)
185 {
186         hammer2_xop_nresolve_t *xop = &arg->xop_nresolve;
187         hammer2_chain_t *parent;
188         hammer2_chain_t *chain;
189         const hammer2_inode_data_t *ripdata;
190         const char *name;
191         size_t name_len;
192         hammer2_key_t key_next;
193         hammer2_key_t lhc;
194         int cache_index = -1;   /* XXX */
195         int error;
196
197         parent = hammer2_inode_chain(xop->head.ip1, clindex,
198                                      HAMMER2_RESOLVE_ALWAYS |
199                                      HAMMER2_RESOLVE_SHARED);
200         if (parent == NULL) {
201                 kprintf("xop_nresolve: NULL parent\n");
202                 chain = NULL;
203                 error = EIO;
204                 goto done;
205         }
206         name = xop->head.name1;
207         name_len = xop->head.name1_len;
208
209         /*
210          * Lookup the directory entry
211          */
212         lhc = hammer2_dirhash(name, name_len);
213         chain = hammer2_chain_lookup(&parent, &key_next,
214                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
215                                      &cache_index,
216                                      HAMMER2_LOOKUP_ALWAYS |
217                                      HAMMER2_LOOKUP_SHARED);
218         while (chain) {
219                 ripdata = &chain->data->ipdata;
220                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
221                     ripdata->meta.name_len == name_len &&
222                     bcmp(ripdata->filename, name, name_len) == 0) {
223                         break;
224                 }
225                 chain = hammer2_chain_next(&parent, chain, &key_next,
226                                            key_next,
227                                            lhc + HAMMER2_DIRHASH_LOMASK,
228                                            &cache_index,
229                                            HAMMER2_LOOKUP_ALWAYS |
230                                            HAMMER2_LOOKUP_SHARED);
231         }
232
233         /*
234          * If the entry is a hardlink pointer, resolve it.
235          */
236         error = 0;
237         if (chain) {
238                 if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
239                         error = hammer2_chain_hardlink_find(
240                                                 xop->head.ip1,
241                                                 &parent, &chain,
242                                                 HAMMER2_LOOKUP_SHARED);
243                 }
244         }
245 done:
246         error = hammer2_xop_feed(&xop->head, chain, clindex, error);
247         if (chain) {
248                 hammer2_chain_unlock(chain);
249                 hammer2_chain_drop(chain);
250         }
251         if (parent) {
252                 hammer2_chain_unlock(parent);
253                 hammer2_chain_drop(parent);
254         }
255 }
256
257 /*
258  * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper
259  * for hammer2_vop_nrename().
260  *
261  * This function locates and removes the directory entry.  If the
262  * entry is a hardlink pointer, this function will also remove the
263  * hardlink target if the target's nlinks is 1.
264  *
265  * The frontend is responsible for moving open inodes to the hidden directory
266  * and for decrementing nlinks.
267  */
268 void
269 hammer2_xop_unlink(hammer2_xop_t *arg, int clindex)
270 {
271         hammer2_xop_unlink_t *xop = &arg->xop_unlink;
272         hammer2_chain_t *parent;
273         hammer2_chain_t *chain;
274         const hammer2_inode_data_t *ripdata;
275         const char *name;
276         size_t name_len;
277         hammer2_key_t key_next;
278         hammer2_key_t lhc;
279         int cache_index = -1;   /* XXX */
280         int error;
281
282         /*
283          * Requires exclusive lock
284          */
285         parent = hammer2_inode_chain(xop->head.ip1, clindex,
286                                      HAMMER2_RESOLVE_ALWAYS);
287         chain = NULL;
288         if (parent == NULL) {
289                 kprintf("xop_nresolve: NULL parent\n");
290                 error = EIO;
291                 goto done;
292         }
293         name = xop->head.name1;
294         name_len = xop->head.name1_len;
295
296         /*
297          * Lookup the directory entry
298          */
299         lhc = hammer2_dirhash(name, name_len);
300         chain = hammer2_chain_lookup(&parent, &key_next,
301                                      lhc, lhc + HAMMER2_DIRHASH_LOMASK,
302                                      &cache_index,
303                                      HAMMER2_LOOKUP_ALWAYS);
304         while (chain) {
305                 ripdata = &chain->data->ipdata;
306                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
307                     ripdata->meta.name_len == name_len &&
308                     bcmp(ripdata->filename, name, name_len) == 0) {
309                         break;
310                 }
311                 chain = hammer2_chain_next(&parent, chain, &key_next,
312                                            key_next,
313                                            lhc + HAMMER2_DIRHASH_LOMASK,
314                                            &cache_index,
315                                            HAMMER2_LOOKUP_ALWAYS);
316         }
317
318         /*
319          * If the directory entry is a HARDLINK pointer then obtain the
320          * underlying file type for the directory typing tests and delete
321          * the HARDLINK pointer chain permanently.  The frontend is left
322          * responsible for handling nlinks on and deleting the actual inode.
323          *
324          * If the directory entry is the actual inode then use its type
325          * for the directory typing tests and delete the chain, permanency
326          * depends on whether the inode is open or not.
327          *
328          * Check directory typing and delete the entry.  Note that
329          * nlinks adjustments are made on the real inode by the frontend,
330          * not here.
331          */
332         error = 0;
333         if (chain) {
334                 int dopermanent = xop->dopermanent;
335                 uint8_t type;
336
337                 type = chain->data->ipdata.meta.type;
338                 if (type == HAMMER2_OBJTYPE_HARDLINK) {
339                         type = chain->data->ipdata.meta.target_type;
340                         dopermanent |= HAMMER2_DELETE_PERMANENT;
341                 }
342
343                 if (type == HAMMER2_OBJTYPE_DIRECTORY &&
344                     checkdirempty(chain) != 0) {
345                         error = ENOTEMPTY;
346                 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
347                     xop->isdir == 0) {
348                         error = ENOTDIR;
349                 } else 
350                 if (type != HAMMER2_OBJTYPE_DIRECTORY &&
351                     xop->isdir >= 1) {
352                         error = EISDIR;
353                 } else {
354                         /*
355                          * This deletes the directory entry itself, which is
356                          * also the inode when nlinks == 1.  Hardlink targets
357                          * are handled in the next conditional.
358                          */
359                         error = chain->error;
360                         hammer2_chain_delete(parent, chain,
361                                              xop->head.mtid, dopermanent);
362                 }
363         }
364
365         /*
366          * If the entry is a hardlink pointer, resolve it.  If this is the
367          * last link, delete it.  The frontend has the master copy of nlinks
368          * but we still have to make adjustments here to synchronize with it.
369          *
370          * On delete / adjust nlinks if there is no error.  But we still need
371          * to resolve the hardlink to feed the inode's real chain back to
372          * the frontend.
373          *
374          * XXX we are basically tracking the nlinks count by doing a delta
375          *     adjustment instead of having the frontend pass the absolute
376          *     value down.  We really need to have the frontend pass the
377          *     absolute value down (difficult because there might not be
378          *     an 'ip').  See also hammer2_xop_nlink().
379          */
380         if (chain &&
381             chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) {
382                 int error2;
383
384                 error2 = hammer2_chain_hardlink_find(xop->head.ip1,
385                                                      &parent, &chain, 0);
386                 if (chain && error == 0 && error2 == 0 &&
387                     (int64_t)chain->data->ipdata.meta.nlinks <= 1) {
388                         hammer2_chain_delete(parent, chain,
389                                              xop->head.mtid,
390                                              xop->dopermanent);
391                 } else if (chain && error == 0 && error2 == 0) {
392                         hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
393                         --chain->data->ipdata.meta.nlinks;
394                 }
395                 if (error == 0)
396                         error = error2;
397         }
398
399         /*
400          * Chains passed to feed are expected to be locked shared.
401          */
402         if (chain)
403                 hammer2_chain_lock_downgrade(chain);
404
405         /*
406          * We always return the hardlink target (the real inode) for
407          * further action.
408          */
409 done:
410         hammer2_xop_feed(&xop->head, chain, clindex, error);
411         if (chain) {
412                 hammer2_chain_unlock(chain);
413                 hammer2_chain_drop(chain);
414                 chain = NULL;
415         }
416         if (parent) {
417                 hammer2_chain_unlock(parent);
418                 hammer2_chain_drop(parent);
419                 parent = NULL;
420         }
421 }
422
423 #if 0
424 /*
425  * Backend for hammer2_vop_nlink() and hammer2_vop_nrename()
426  *
427  * ip1 - fdip
428  * ip2 - ip
429  * ip3 - cdip
430  *
431  * If a hardlink pointer:
432  *      The existing hardlink target {fdip,ip} must be moved to another
433  *      directory {cdip,ip}
434  *
435  * If not a hardlink pointer:
436  *      Convert the target {fdip,ip} to a hardlink target {cdip,ip} and
437  *      replace the original namespace {fdip,name} with a hardlink pointer.
438  */
439 void
440 hammer2_xop_nlink(hammer2_xop_t *arg, int clindex)
441 {
442         hammer2_xop_nlink_t *xop = &arg->xop_nlink;
443         hammer2_pfs_t *pmp;
444         hammer2_inode_data_t *wipdata;
445         hammer2_chain_t *parent;
446         hammer2_chain_t *chain;
447         hammer2_chain_t *tmp;
448         hammer2_inode_t *ip;
449         hammer2_key_t key_dummy;
450         int cache_index = -1;
451         int error;
452         int did_delete = 0;
453
454         /*
455          * We need the precise parent chain to issue the deletion.
456          */
457         ip = xop->head.ip2;
458         pmp = ip->pmp;
459         parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
460         if (parent)
461                 hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS);
462         if (parent == NULL) {
463                 chain = NULL;
464                 error = EIO;
465                 goto done;
466         }
467         chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS);
468         if (chain == NULL) {
469                 error = EIO;
470                 goto done;
471         }
472         KKASSERT(chain->parent == parent);
473
474         if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) {
475                 /*
476                  * Delete the original chain and hold onto it for the move
477                  * to cdir.
478                  *
479                  * Replace the namespace with a hardlink pointer if the
480                  * chain being moved is not already a hardlink target.
481                  */
482                 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
483                 did_delete = 1;
484
485                 tmp = NULL;
486                 error = hammer2_chain_create(&parent, &tmp, pmp,
487                                              chain->bref.key, 0,
488                                              HAMMER2_BREF_TYPE_INODE,
489                                              HAMMER2_INODE_BYTES,
490                                              xop->head.mtid, 0, 0);
491                 if (error)
492                         goto done;
493                 hammer2_chain_modify(tmp, xop->head.mtid, 0, 0);
494                 wipdata = &tmp->data->ipdata;
495                 bzero(wipdata, sizeof(*wipdata));
496                 wipdata->meta.name_key = chain->data->ipdata.meta.name_key;
497                 wipdata->meta.name_len = chain->data->ipdata.meta.name_len;
498                 bcopy(chain->data->ipdata.filename, wipdata->filename,
499                       chain->data->ipdata.meta.name_len);
500                 wipdata->meta.target_type = chain->data->ipdata.meta.type;
501                 wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK;
502                 wipdata->meta.inum = ip->meta.inum;
503                 wipdata->meta.version = HAMMER2_INODE_VERSION_ONE;
504                 wipdata->meta.nlinks = 1;
505                 wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA;
506
507                 hammer2_chain_unlock(tmp);
508                 hammer2_chain_drop(tmp);
509         } else if (xop->head.ip1 != xop->head.ip3) {
510                 /*
511                  * Delete the hardlink target so it can be moved
512                  * to cdir.
513                  */
514                 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
515                 did_delete = 1;
516         } else {
517                 /*
518                  * Deletion not necessary (just a nlinks update).
519                  */
520                 did_delete = 0;
521         }
522
523         hammer2_chain_unlock(parent);
524         hammer2_chain_drop(parent);
525         parent = NULL;
526
527         /*
528          * Ok, back to the deleted chain.  We must reconnect this chain
529          * as a hardlink target to cdir (ip3).
530          *
531          * WARNING! Frontend assumes filename length is 18 bytes.
532          */
533         if (did_delete) {
534                 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
535                 wipdata = &chain->data->ipdata;
536                 ksnprintf(wipdata->filename, sizeof(wipdata->filename),
537                           "0x%016jx", (intmax_t)ip->meta.inum);
538                 wipdata->meta.name_len = strlen(wipdata->filename);
539                 wipdata->meta.name_key = ip->meta.inum;
540
541                 /*
542                  * We must seek parent properly for the create to reattach
543                  * chain.  XXX just use chain->parent or
544                  * inode_chain_and_parent() ?
545                  */
546                 parent = hammer2_inode_chain(xop->head.ip3, clindex,
547                                              HAMMER2_RESOLVE_ALWAYS);
548                 if (parent == NULL) {
549                         error = EIO;
550                         goto done;
551                 }
552                 tmp = hammer2_chain_lookup(&parent, &key_dummy,
553                                            ip->meta.inum, ip->meta.inum,
554                                            &cache_index, 0);
555                 if (tmp) {
556                         hammer2_chain_unlock(tmp);
557                         hammer2_chain_drop(tmp);
558                         error = EEXIST;
559                         goto done;
560                 }
561                 error = hammer2_chain_create(&parent, &chain, pmp,
562                                              wipdata->meta.name_key, 0,
563                                              HAMMER2_BREF_TYPE_INODE,
564                                              HAMMER2_INODE_BYTES,
565                                              xop->head.mtid, 0, 0);
566         } else {
567                 error = 0;
568         }
569
570         /*
571          * Bump nlinks to synchronize with frontend.
572          */
573         if (xop->nlinks_delta) {
574                 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
575                 chain->data->ipdata.meta.nlinks += xop->nlinks_delta;
576         }
577
578         /*
579          * To avoid having to scan the collision space we can simply
580          * reuse the inode's original name_key.  But ip->meta.name_key
581          * may have already been updated by the front-end, so use xop->lhc.
582          *
583          * (frontend is responsible for fixing up ip->pip).
584          */
585 done:
586         hammer2_xop_feed(&xop->head, NULL, clindex, error);
587         if (parent) {
588                 hammer2_chain_unlock(parent);
589                 hammer2_chain_drop(parent);
590         }
591         if (chain) {
592                 hammer2_chain_unlock(chain);
593                 hammer2_chain_drop(chain);
594         }
595 }
596 #endif
597
598 /*
599  * Backend for hammer2_vop_nrename()
600  *
601  * This handles the final step of renaming, either renaming the
602  * actual inode or renaming the hardlink pointer.
603  */
604 void
605 hammer2_xop_nrename(hammer2_xop_t *arg, int clindex)
606 {
607         hammer2_xop_nrename_t *xop = &arg->xop_nrename;
608         hammer2_pfs_t *pmp;
609         hammer2_chain_t *parent;
610         hammer2_chain_t *chain;
611         hammer2_chain_t *tmp;
612         hammer2_inode_t *ip;
613         hammer2_key_t key_dummy;
614         int cache_index = -1;
615         int error;
616
617         /*
618          * We need the precise parent chain to issue the deletion.
619          *
620          * If this is not a hardlink target we can act on the inode,
621          * otherwise we have to locate the hardlink pointer.
622          */
623         ip = xop->head.ip2;
624         pmp = ip->pmp;
625         chain = NULL;
626
627         if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
628                 /*
629                  * Find ip's direct parent chain.
630                  */
631                 parent = hammer2_inode_chain(ip, clindex,
632                                              HAMMER2_RESOLVE_ALWAYS);
633                 if (parent)
634                         hammer2_chain_getparent(&parent,
635                                                 HAMMER2_RESOLVE_ALWAYS);
636                 if (parent == NULL) {
637                         error = EIO;
638                         goto done;
639                 }
640                 chain = hammer2_inode_chain(ip, clindex,
641                                             HAMMER2_RESOLVE_ALWAYS);
642                 if (chain == NULL) {
643                         error = EIO;
644                         goto done;
645                 }
646         } else {
647                 /*
648                  * The hardlink pointer for the head.ip1 hardlink target
649                  * is in fdip, do a namespace search.
650                  */
651                 const hammer2_inode_data_t *ripdata;
652                 hammer2_key_t lhc;
653                 hammer2_key_t key_next;
654                 const char *name;
655                 size_t name_len;
656
657                 parent = hammer2_inode_chain(xop->head.ip1, clindex,
658                                              HAMMER2_RESOLVE_ALWAYS);
659                 if (parent == NULL) {
660                         kprintf("xop_nrename: NULL parent\n");
661                         error = EIO;
662                         goto done;
663                 }
664                 name = xop->head.name1;
665                 name_len = xop->head.name1_len;
666
667                 /*
668                  * Lookup the directory entry
669                  */
670                 lhc = hammer2_dirhash(name, name_len);
671                 chain = hammer2_chain_lookup(&parent, &key_next,
672                                              lhc, lhc + HAMMER2_DIRHASH_LOMASK,
673                                              &cache_index,
674                                              HAMMER2_LOOKUP_ALWAYS);
675                 while (chain) {
676                         ripdata = &chain->data->ipdata;
677                         if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
678                             ripdata->meta.name_len == name_len &&
679                             bcmp(ripdata->filename, name, name_len) == 0) {
680                                 break;
681                         }
682                         chain = hammer2_chain_next(&parent, chain, &key_next,
683                                                    key_next,
684                                                    lhc + HAMMER2_DIRHASH_LOMASK,
685                                                    &cache_index,
686                                                    HAMMER2_LOOKUP_ALWAYS);
687                 }
688         }
689
690         if (chain == NULL) {
691                 /* XXX shouldn't happen, but does under fsstress */
692                 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\"  ENOENT\n",
693                         xop->head.name1,
694                         xop->head.name2);
695                 error = ENOENT;
696                 goto done;
697         }
698
699         /*
700          * Delete it, then create it in the new namespace.
701          */
702         hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
703         hammer2_chain_unlock(parent);
704         hammer2_chain_drop(parent);
705         parent = NULL;          /* safety */
706
707         /*
708          * Ok, back to the deleted chain.  We must reconnect this chain
709          * to tdir (ip3).  The chain (a real inode or a hardlink pointer)
710          * is not otherwise modified.
711          *
712          * Frontend is expected to replicate the same inode meta data
713          * modifications.
714          *
715          * NOTE!  This chain may not represent the actual inode, it
716          *        can be a hardlink pointer.
717          *
718          * XXX in-inode parent directory specification?
719          */
720         if (chain->data->ipdata.meta.name_key != xop->lhc ||
721             xop->head.name1_len != xop->head.name2_len ||
722             bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
723                 hammer2_inode_data_t *wipdata;
724
725                 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
726                 wipdata = &chain->data->ipdata;
727
728                 bzero(wipdata->filename, sizeof(wipdata->filename));
729                 bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len);
730                 wipdata->meta.name_key = xop->lhc;
731                 wipdata->meta.name_len = xop->head.name2_len;
732         }
733
734         /*
735          * We must seek parent properly for the create.
736          */
737         parent = hammer2_inode_chain(xop->head.ip3, clindex,
738                                      HAMMER2_RESOLVE_ALWAYS);
739         if (parent == NULL) {
740                 error = EIO;
741                 goto done;
742         }
743         tmp = hammer2_chain_lookup(&parent, &key_dummy,
744                                    xop->lhc, xop->lhc,
745                                    &cache_index, 0);
746         if (tmp) {
747                 hammer2_chain_unlock(tmp);
748                 hammer2_chain_drop(tmp);
749                 error = EEXIST;
750                 goto done;
751         }
752
753         error = hammer2_chain_create(&parent, &chain, pmp,
754                                      xop->lhc, 0,
755                                      HAMMER2_BREF_TYPE_INODE,
756                                      HAMMER2_INODE_BYTES,
757                                      xop->head.mtid, 0, 0);
758         /*
759          * (frontend is responsible for fixing up ip->pip).
760          */
761 done:
762         hammer2_xop_feed(&xop->head, NULL, clindex, error);
763         if (parent) {
764                 hammer2_chain_unlock(parent);
765                 hammer2_chain_drop(parent);
766         }
767         if (chain) {
768                 hammer2_chain_unlock(chain);
769                 hammer2_chain_drop(chain);
770         }
771 }
772
773 /*
774  * Directory collision resolver scan helper (backend, threaded).
775  *
776  * Used by the inode create code to locate an unused lhc.
777  */
778 void
779 hammer2_xop_scanlhc(hammer2_xop_t *arg, int clindex)
780 {
781         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
782         hammer2_chain_t *parent;
783         hammer2_chain_t *chain;
784         hammer2_key_t key_next;
785         int cache_index = -1;   /* XXX */
786         int error = 0;
787
788         parent = hammer2_inode_chain(xop->head.ip1, clindex,
789                                      HAMMER2_RESOLVE_ALWAYS |
790                                      HAMMER2_RESOLVE_SHARED);
791         if (parent == NULL) {
792                 kprintf("xop_nresolve: NULL parent\n");
793                 chain = NULL;
794                 error = EIO;
795                 goto done;
796         }
797
798         /*
799          * Lookup all possibly conflicting directory entries, the feed
800          * inherits the chain's lock so do not unlock it on the iteration.
801          */
802         chain = hammer2_chain_lookup(&parent, &key_next,
803                                      xop->lhc,
804                                      xop->lhc + HAMMER2_DIRHASH_LOMASK,
805                                      &cache_index,
806                                      HAMMER2_LOOKUP_ALWAYS |
807                                      HAMMER2_LOOKUP_SHARED);
808         while (chain) {
809                 error = hammer2_xop_feed(&xop->head, chain, clindex,
810                                          chain->error);
811                 if (error) {
812                         hammer2_chain_unlock(chain);
813                         hammer2_chain_drop(chain);
814                         chain = NULL;   /* safety */
815                         break;
816                 }
817                 chain = hammer2_chain_next(&parent, chain, &key_next,
818                                            key_next,
819                                            xop->lhc + HAMMER2_DIRHASH_LOMASK,
820                                            &cache_index,
821                                            HAMMER2_LOOKUP_ALWAYS |
822                                            HAMMER2_LOOKUP_SHARED);
823         }
824 done:
825         hammer2_xop_feed(&xop->head, NULL, clindex, error);
826         if (parent) {
827                 hammer2_chain_unlock(parent);
828                 hammer2_chain_drop(parent);
829         }
830 }
831
832 /*
833  * Generic lookup of a specific key.
834  *
835  * Used by the inode hidden directory code to find the hidden directory.
836  */
837 void
838 hammer2_xop_lookup(hammer2_xop_t *arg, int clindex)
839 {
840         hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
841         hammer2_chain_t *parent;
842         hammer2_chain_t *chain;
843         hammer2_key_t key_next;
844         int cache_index = -1;   /* XXX */
845         int error = 0;
846
847         parent = hammer2_inode_chain(xop->head.ip1, clindex,
848                                      HAMMER2_RESOLVE_ALWAYS |
849                                      HAMMER2_RESOLVE_SHARED);
850         chain = NULL;
851         if (parent == NULL) {
852                 error = EIO;
853                 goto done;
854         }
855
856         /*
857          * Lookup all possibly conflicting directory entries, the feed
858          * inherits the chain's lock so do not unlock it on the iteration.
859          */
860         chain = hammer2_chain_lookup(&parent, &key_next,
861                                      xop->lhc, xop->lhc,
862                                      &cache_index,
863                                      HAMMER2_LOOKUP_ALWAYS |
864                                      HAMMER2_LOOKUP_SHARED);
865         if (chain)
866                 hammer2_xop_feed(&xop->head, chain, clindex, chain->error);
867         else
868                 hammer2_xop_feed(&xop->head, NULL, clindex, ENOENT);
869
870 done:
871         if (chain) {
872                 hammer2_chain_unlock(chain);
873                 hammer2_chain_drop(chain);
874         }
875         if (parent) {
876                 hammer2_chain_unlock(parent);
877                 hammer2_chain_drop(parent);
878         }
879 }
880
881 /*
882  * Generic scan
883  *
884  * WARNING! Fed chains must be locked shared so ownership can be transfered
885  *          and to prevent frontend/backend stalls that would occur with an
886  *          exclusive lock.  The shared lock also allows chain->data to be
887  *          retained.
888  */
889 void
890 hammer2_xop_scanall(hammer2_xop_t *arg, int clindex)
891 {
892         hammer2_xop_scanall_t *xop = &arg->xop_scanall;
893         hammer2_chain_t *parent;
894         hammer2_chain_t *chain;
895         hammer2_key_t key_next;
896         int cache_index = -1;
897         int error = 0;
898
899         /*
900          * Assert required flags.
901          */
902         KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED);
903         KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED);
904
905         /*
906          * The inode's chain is the iterator.  If we cannot acquire it our
907          * contribution ends here.
908          */
909         parent = hammer2_inode_chain(xop->head.ip1, clindex,
910                                      xop->resolve_flags);
911         if (parent == NULL) {
912                 kprintf("xop_readdir: NULL parent\n");
913                 goto done;
914         }
915
916         /*
917          * Generic scan of exact records.  Note that indirect blocks are
918          * automatically recursed and will not be returned.
919          */
920         chain = hammer2_chain_lookup(&parent, &key_next,
921                                      xop->key_beg, xop->key_end,
922                                      &cache_index, xop->lookup_flags);
923         while (chain) {
924                 error = hammer2_xop_feed(&xop->head, chain, clindex, 0);
925                 if (error)
926                         break;
927                 chain = hammer2_chain_next(&parent, chain, &key_next,
928                                            key_next, xop->key_end,
929                                            &cache_index, xop->lookup_flags);
930         }
931         if (chain) {
932                 hammer2_chain_unlock(chain);
933                 hammer2_chain_drop(chain);
934         }
935         hammer2_chain_unlock(parent);
936         hammer2_chain_drop(parent);
937 done:
938         hammer2_xop_feed(&xop->head, NULL, clindex, error);
939 }