hammer2 - Embed cache_index heuristic in chain structure
[dragonfly.git] / sys / vfs / hammer2 / hammer2_ioctl.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  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 /*
36  * Ioctl Functions.
37  *
38  * WARNING! The ioctl functions which manipulate the connection state need
39  *          to be able to run without deadlock on the volume's chain lock.
40  *          Most of these functions use a separate lock.
41  */
42
43 #include "hammer2.h"
44
45 static int hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data);
46 static int hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data);
47 static int hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data);
48 static int hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data);
49 static int hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data);
50 static int hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data);
51 static int hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data);
52 static int hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data);
53 static int hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data);
54 static int hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data);
55 static int hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data);
56 static int hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data);
57 static int hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data);
58 static int hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data);
59 static int hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data);
60 static int hammer2_ioctl_debug_dump(hammer2_inode_t *ip);
61 //static int hammer2_ioctl_inode_comp_set(hammer2_inode_t *ip, void *data);
62 //static int hammer2_ioctl_inode_comp_rec_set(hammer2_inode_t *ip, void *data);
63 //static int hammer2_ioctl_inode_comp_rec_set2(hammer2_inode_t *ip, void *data);
64 static int hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data);
65
66 int
67 hammer2_ioctl(hammer2_inode_t *ip, u_long com, void *data, int fflag,
68               struct ucred *cred)
69 {
70         int error;
71
72         /*
73          * Standard root cred checks, will be selectively ignored below
74          * for ioctls that do not require root creds.
75          */
76         error = priv_check_cred(cred, PRIV_HAMMER_IOCTL, 0);
77
78         switch(com) {
79         case HAMMER2IOC_VERSION_GET:
80                 error = hammer2_ioctl_version_get(ip, data);
81                 break;
82         case HAMMER2IOC_RECLUSTER:
83                 if (error == 0)
84                         error = hammer2_ioctl_recluster(ip, data);
85                 break;
86         case HAMMER2IOC_REMOTE_SCAN:
87                 if (error == 0)
88                         error = hammer2_ioctl_remote_scan(ip, data);
89                 break;
90         case HAMMER2IOC_REMOTE_ADD:
91                 if (error == 0)
92                         error = hammer2_ioctl_remote_add(ip, data);
93                 break;
94         case HAMMER2IOC_REMOTE_DEL:
95                 if (error == 0)
96                         error = hammer2_ioctl_remote_del(ip, data);
97                 break;
98         case HAMMER2IOC_REMOTE_REP:
99                 if (error == 0)
100                         error = hammer2_ioctl_remote_rep(ip, data);
101                 break;
102         case HAMMER2IOC_SOCKET_GET:
103                 if (error == 0)
104                         error = hammer2_ioctl_socket_get(ip, data);
105                 break;
106         case HAMMER2IOC_SOCKET_SET:
107                 if (error == 0)
108                         error = hammer2_ioctl_socket_set(ip, data);
109                 break;
110         case HAMMER2IOC_PFS_GET:
111                 if (error == 0)
112                         error = hammer2_ioctl_pfs_get(ip, data);
113                 break;
114         case HAMMER2IOC_PFS_LOOKUP:
115                 if (error == 0)
116                         error = hammer2_ioctl_pfs_lookup(ip, data);
117                 break;
118         case HAMMER2IOC_PFS_CREATE:
119                 if (error == 0)
120                         error = hammer2_ioctl_pfs_create(ip, data);
121                 break;
122         case HAMMER2IOC_PFS_DELETE:
123                 if (error == 0)
124                         error = hammer2_ioctl_pfs_delete(ip, data);
125                 break;
126         case HAMMER2IOC_PFS_SNAPSHOT:
127                 if (error == 0)
128                         error = hammer2_ioctl_pfs_snapshot(ip, data);
129                 break;
130         case HAMMER2IOC_INODE_GET:
131                 error = hammer2_ioctl_inode_get(ip, data);
132                 break;
133         case HAMMER2IOC_INODE_SET:
134                 if (error == 0)
135                         error = hammer2_ioctl_inode_set(ip, data);
136                 break;
137         case HAMMER2IOC_BULKFREE_SCAN:
138                 error = hammer2_ioctl_bulkfree_scan(ip, data);
139                 break;
140         case HAMMER2IOC_BULKFREE_ASYNC:
141                 error = hammer2_ioctl_bulkfree_scan(ip, NULL);
142                 break;
143         /*case HAMMER2IOC_INODE_COMP_SET:
144                 error = hammer2_ioctl_inode_comp_set(ip, data);
145                 break;
146         case HAMMER2IOC_INODE_COMP_REC_SET:
147                 error = hammer2_ioctl_inode_comp_rec_set(ip, data);
148                 break;
149         case HAMMER2IOC_INODE_COMP_REC_SET2:
150                 error = hammer2_ioctl_inode_comp_rec_set2(ip, data);
151                 break;*/
152         case HAMMER2IOC_DEBUG_DUMP:
153                 error = hammer2_ioctl_debug_dump(ip);
154                 break;
155         default:
156                 error = EOPNOTSUPP;
157                 break;
158         }
159         return (error);
160 }
161
162 /*
163  * Retrieve version and basic info
164  */
165 static int
166 hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data)
167 {
168         hammer2_ioc_version_t *version = data;
169         hammer2_dev_t *hmp;
170
171         hmp = ip->pmp->pfs_hmps[0];
172         if (hmp)
173                 version->version = hmp->voldata.version;
174         else
175                 version->version = -1;
176         return 0;
177 }
178
179 static int
180 hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data)
181 {
182         hammer2_ioc_recluster_t *recl = data;
183         struct vnode *vproot;
184         struct file *fp;
185         hammer2_cluster_t *cluster;
186         int error;
187
188         fp = holdfp(curproc->p_fd, recl->fd, -1);
189         if (fp) {
190                 error = VFS_ROOT(ip->pmp->mp, &vproot);
191                 if (error == 0) {
192                         cluster = &ip->pmp->iroot->cluster;
193                         kprintf("reconnect to cluster: nc=%d focus=%p\n",
194                                 cluster->nchains, cluster->focus);
195                         if (cluster->nchains != 1 || cluster->focus == NULL) {
196                                 kprintf("not a local device mount\n");
197                                 error = EINVAL;
198                         } else {
199                                 hammer2_cluster_reconnect(cluster->focus->hmp,
200                                                           fp);
201                                 kprintf("ok\n");
202                                 error = 0;
203                         }
204                         vput(vproot);
205                 }
206         } else {
207                 error = EINVAL;
208         }
209         return error;
210 }
211
212 /*
213  * Retrieve information about a remote
214  */
215 static int
216 hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data)
217 {
218         hammer2_dev_t *hmp;
219         hammer2_ioc_remote_t *remote = data;
220         int copyid = remote->copyid;
221
222         hmp = ip->pmp->pfs_hmps[0];
223         if (hmp == NULL)
224                 return (EINVAL);
225
226         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
227                 return (EINVAL);
228
229         hammer2_voldata_lock(hmp);
230         remote->copy1 = hmp->voldata.copyinfo[copyid];
231         hammer2_voldata_unlock(hmp);
232
233         /*
234          * Adjust nextid (GET only)
235          */
236         while (++copyid < HAMMER2_COPYID_COUNT &&
237                hmp->voldata.copyinfo[copyid].copyid == 0) {
238                 ;
239         }
240         if (copyid == HAMMER2_COPYID_COUNT)
241                 remote->nextid = -1;
242         else
243                 remote->nextid = copyid;
244
245         return(0);
246 }
247
248 /*
249  * Add new remote entry
250  */
251 static int
252 hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data)
253 {
254         hammer2_ioc_remote_t *remote = data;
255         hammer2_pfs_t *pmp = ip->pmp;
256         hammer2_dev_t *hmp;
257         int copyid = remote->copyid;
258         int error = 0;
259
260         hmp = pmp->pfs_hmps[0];
261         if (hmp == NULL)
262                 return (EINVAL);
263         if (copyid >= HAMMER2_COPYID_COUNT)
264                 return (EINVAL);
265
266         hammer2_voldata_lock(hmp);
267         if (copyid < 0) {
268                 for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
269                         if (hmp->voldata.copyinfo[copyid].copyid == 0)
270                                 break;
271                 }
272                 if (copyid == HAMMER2_COPYID_COUNT) {
273                         error = ENOSPC;
274                         goto failed;
275                 }
276         }
277         hammer2_voldata_modify(hmp);
278         remote->copy1.copyid = copyid;
279         hmp->voldata.copyinfo[copyid] = remote->copy1;
280         hammer2_volconf_update(hmp, copyid);
281 failed:
282         hammer2_voldata_unlock(hmp);
283         return (error);
284 }
285
286 /*
287  * Delete existing remote entry
288  */
289 static int
290 hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data)
291 {
292         hammer2_ioc_remote_t *remote = data;
293         hammer2_pfs_t *pmp = ip->pmp;
294         hammer2_dev_t *hmp;
295         int copyid = remote->copyid;
296         int error = 0;
297
298         hmp = pmp->pfs_hmps[0];
299         if (hmp == NULL)
300                 return (EINVAL);
301         if (copyid >= HAMMER2_COPYID_COUNT)
302                 return (EINVAL);
303         remote->copy1.path[sizeof(remote->copy1.path) - 1] = 0;
304         hammer2_voldata_lock(hmp);
305         if (copyid < 0) {
306                 for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
307                         if (hmp->voldata.copyinfo[copyid].copyid == 0)
308                                 continue;
309                         if (strcmp(remote->copy1.path,
310                             hmp->voldata.copyinfo[copyid].path) == 0) {
311                                 break;
312                         }
313                 }
314                 if (copyid == HAMMER2_COPYID_COUNT) {
315                         error = ENOENT;
316                         goto failed;
317                 }
318         }
319         hammer2_voldata_modify(hmp);
320         hmp->voldata.copyinfo[copyid].copyid = 0;
321         hammer2_volconf_update(hmp, copyid);
322 failed:
323         hammer2_voldata_unlock(hmp);
324         return (error);
325 }
326
327 /*
328  * Replace existing remote entry
329  */
330 static int
331 hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data)
332 {
333         hammer2_ioc_remote_t *remote = data;
334         hammer2_dev_t *hmp;
335         int copyid = remote->copyid;
336
337         hmp = ip->pmp->pfs_hmps[0];
338         if (hmp == NULL)
339                 return (EINVAL);
340         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
341                 return (EINVAL);
342
343         hammer2_voldata_lock(hmp);
344         hammer2_voldata_modify(hmp);
345         /*hammer2_volconf_update(hmp, copyid);*/
346         hammer2_voldata_unlock(hmp);
347
348         return(0);
349 }
350
351 /*
352  * Retrieve communications socket
353  */
354 static int
355 hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data)
356 {
357         return (EOPNOTSUPP);
358 }
359
360 /*
361  * Set communications socket for connection
362  */
363 static int
364 hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data)
365 {
366         hammer2_ioc_remote_t *remote = data;
367         hammer2_dev_t *hmp;
368         int copyid = remote->copyid;
369
370         hmp = ip->pmp->pfs_hmps[0];
371         if (hmp == NULL)
372                 return (EINVAL);
373         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
374                 return (EINVAL);
375
376         hammer2_voldata_lock(hmp);
377         hammer2_voldata_unlock(hmp);
378
379         return(0);
380 }
381
382 /*
383  * Used to scan and retrieve PFS information.  PFS's are directories under
384  * the super-root.
385  *
386  * To scan PFSs pass name_key=0.  The function will scan for the next
387  * PFS and set all fields, as well as set name_next to the next key.
388  * When no PFSs remain, name_next is set to (hammer2_key_t)-1.
389  *
390  * To retrieve a particular PFS by key, specify the key but note that
391  * the ioctl will return the lowest key >= specified_key, so the caller
392  * must verify the key.
393  *
394  * To retrieve the PFS associated with the file descriptor, pass
395  * name_key set to (hammer2_key_t)-1.
396  */
397 static int
398 hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data)
399 {
400         const hammer2_inode_data_t *ripdata;
401         hammer2_dev_t *hmp;
402         hammer2_ioc_pfs_t *pfs;
403         hammer2_chain_t *parent;
404         hammer2_chain_t *chain;
405         hammer2_key_t key_next;
406         hammer2_key_t save_key;
407         int error;
408
409         hmp = ip->pmp->pfs_hmps[0];
410         if (hmp == NULL)
411                 return (EINVAL);
412
413         pfs = data;
414         save_key = pfs->name_key;
415         error = 0;
416
417         /*
418          * Setup
419          */
420         if (save_key == (hammer2_key_t)-1) {
421                 hammer2_inode_lock(ip->pmp->iroot, 0);
422                 parent = NULL;
423                 chain = hammer2_inode_chain(ip->pmp->iroot, 0,
424                                             HAMMER2_RESOLVE_ALWAYS |
425                                             HAMMER2_RESOLVE_SHARED);
426         } else {
427                 hammer2_inode_lock(hmp->spmp->iroot, 0);
428                 parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
429                                             HAMMER2_RESOLVE_ALWAYS |
430                                             HAMMER2_RESOLVE_SHARED);
431                 chain = hammer2_chain_lookup(&parent, &key_next,
432                                             pfs->name_key, HAMMER2_KEY_MAX,
433                                             HAMMER2_LOOKUP_SHARED);
434         }
435
436         /*
437          * Locate next PFS
438          */
439         while (chain) {
440                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE)
441                         break;
442                 if (parent == NULL) {
443                         hammer2_chain_unlock(chain);
444                         hammer2_chain_drop(chain);
445                         chain = NULL;
446                         break;
447                 }
448                 chain = hammer2_chain_next(&parent, chain, &key_next,
449                                             key_next, HAMMER2_KEY_MAX,
450                                             HAMMER2_LOOKUP_SHARED);
451         }
452
453         /*
454          * Load the data being returned by the ioctl.
455          */
456         if (chain) {
457                 ripdata = &chain->data->ipdata;
458                 pfs->name_key = ripdata->meta.name_key;
459                 pfs->pfs_type = ripdata->meta.pfs_type;
460                 pfs->pfs_subtype = ripdata->meta.pfs_subtype;
461                 pfs->pfs_clid = ripdata->meta.pfs_clid;
462                 pfs->pfs_fsid = ripdata->meta.pfs_fsid;
463                 KKASSERT(ripdata->meta.name_len < sizeof(pfs->name));
464                 bcopy(ripdata->filename, pfs->name, ripdata->meta.name_len);
465                 pfs->name[ripdata->meta.name_len] = 0;
466                 ripdata = NULL; /* safety */
467
468                 /*
469                  * Calculate name_next, if any.
470                  */
471                 if (parent == NULL) {
472                         pfs->name_next = (hammer2_key_t)-1;
473                 } else {
474                         chain = hammer2_chain_next(&parent, chain, &key_next,
475                                                     key_next, HAMMER2_KEY_MAX,
476                                                     HAMMER2_LOOKUP_SHARED);
477                         if (chain)
478                                 pfs->name_next = chain->bref.key;
479                         else
480                                 pfs->name_next = (hammer2_key_t)-1;
481                 }
482         } else {
483                 pfs->name_next = (hammer2_key_t)-1;
484                 error = ENOENT;
485         }
486
487         /*
488          * Cleanup
489          */
490         if (chain) {
491                 hammer2_chain_unlock(chain);
492                 hammer2_chain_drop(chain);
493         }
494         if (parent) {
495                 hammer2_chain_unlock(parent);
496                 hammer2_chain_drop(parent);
497         }
498         if (save_key == (hammer2_key_t)-1) {
499                 hammer2_inode_unlock(ip->pmp->iroot);
500         } else {
501                 hammer2_inode_unlock(hmp->spmp->iroot);
502         }
503
504         return (error);
505 }
506
507 /*
508  * Find a specific PFS by name
509  */
510 static int
511 hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data)
512 {
513         const hammer2_inode_data_t *ripdata;
514         hammer2_dev_t *hmp;
515         hammer2_ioc_pfs_t *pfs;
516         hammer2_chain_t *parent;
517         hammer2_chain_t *chain;
518         hammer2_key_t key_next;
519         hammer2_key_t lhc;
520         int error;
521         size_t len;
522
523         hmp = ip->pmp->pfs_hmps[0];
524         if (hmp == NULL)
525                 return (EINVAL);
526
527         pfs = data;
528         error = 0;
529
530         hammer2_inode_lock(hmp->spmp->iroot, HAMMER2_RESOLVE_SHARED);
531         parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
532                                      HAMMER2_RESOLVE_ALWAYS |
533                                      HAMMER2_RESOLVE_SHARED);
534
535         pfs->name[sizeof(pfs->name) - 1] = 0;
536         len = strlen(pfs->name);
537         lhc = hammer2_dirhash(pfs->name, len);
538
539         chain = hammer2_chain_lookup(&parent, &key_next,
540                                          lhc, lhc + HAMMER2_DIRHASH_LOMASK,
541                                          HAMMER2_LOOKUP_SHARED);
542         while (chain) {
543                 if (hammer2_chain_dirent_test(chain, pfs->name, len))
544                         break;
545                 chain = hammer2_chain_next(&parent, chain, &key_next,
546                                            key_next,
547                                            lhc + HAMMER2_DIRHASH_LOMASK,
548                                            HAMMER2_LOOKUP_SHARED);
549         }
550
551         /*
552          * Load the data being returned by the ioctl.
553          */
554         if (chain) {
555                 KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
556                 ripdata = &chain->data->ipdata;
557                 pfs->name_key = ripdata->meta.name_key;
558                 pfs->pfs_type = ripdata->meta.pfs_type;
559                 pfs->pfs_subtype = ripdata->meta.pfs_subtype;
560                 pfs->pfs_clid = ripdata->meta.pfs_clid;
561                 pfs->pfs_fsid = ripdata->meta.pfs_fsid;
562                 ripdata = NULL;
563
564                 hammer2_chain_unlock(chain);
565                 hammer2_chain_drop(chain);
566         } else {
567                 error = ENOENT;
568         }
569         if (parent) {
570                 hammer2_chain_unlock(parent);
571                 hammer2_chain_drop(parent);
572         }
573         hammer2_inode_unlock(hmp->spmp->iroot);
574
575         return (error);
576 }
577
578 /*
579  * Create a new PFS under the super-root
580  */
581 static int
582 hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data)
583 {
584         hammer2_inode_data_t *nipdata;
585         hammer2_chain_t *nchain;
586         hammer2_dev_t *hmp;
587         hammer2_dev_t *force_local;
588         hammer2_ioc_pfs_t *pfs;
589         hammer2_inode_t *nip;
590         hammer2_tid_t mtid;
591         int error;
592
593         hmp = ip->pmp->pfs_hmps[0];     /* XXX */
594         if (hmp == NULL)
595                 return (EINVAL);
596
597         pfs = data;
598         nip = NULL;
599
600         if (pfs->name[0] == 0)
601                 return(EINVAL);
602         pfs->name[sizeof(pfs->name) - 1] = 0;   /* ensure 0-termination */
603
604         if (hammer2_ioctl_pfs_lookup(ip, pfs) == 0)
605                 return(EEXIST);
606
607         hammer2_trans_init(hmp->spmp, 0);
608         mtid = hammer2_trans_sub(hmp->spmp);
609         nip = hammer2_inode_create(hmp->spmp->iroot, hmp->spmp->iroot,
610                                    NULL, NULL,
611                                    pfs->name, strlen(pfs->name), 0,
612                                    1, HAMMER2_OBJTYPE_DIRECTORY, 0,
613                                    HAMMER2_INSERT_PFSROOT, &error);
614         if (error == 0) {
615                 hammer2_inode_modify(nip);
616                 nchain = hammer2_inode_chain(nip, 0, HAMMER2_RESOLVE_ALWAYS);
617                 hammer2_chain_modify(nchain, mtid, 0, 0);
618                 nipdata = &nchain->data->ipdata;
619
620                 nip->meta.pfs_type = pfs->pfs_type;
621                 nip->meta.pfs_subtype = pfs->pfs_subtype;
622                 nip->meta.pfs_clid = pfs->pfs_clid;
623                 nip->meta.pfs_fsid = pfs->pfs_fsid;
624                 nip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT;
625
626                 /*
627                  * Set default compression and check algorithm.  This
628                  * can be changed later.
629                  *
630                  * Do not allow compression on PFS's with the special name
631                  * "boot", the boot loader can't decompress (yet).
632                  */
633                 nip->meta.comp_algo =
634                         HAMMER2_ENC_ALGO(HAMMER2_COMP_NEWFS_DEFAULT);
635                 nip->meta.check_algo =
636                         HAMMER2_ENC_ALGO( HAMMER2_CHECK_XXHASH64);
637
638                 if (strcasecmp(pfs->name, "boot") == 0) {
639                         nip->meta.comp_algo =
640                                 HAMMER2_ENC_ALGO(HAMMER2_COMP_AUTOZERO);
641                 }
642
643                 /*
644                  * Super-root isn't mounted, fsync it
645                  */
646                 hammer2_chain_unlock(nchain);
647                 hammer2_inode_ref(nip);
648                 hammer2_inode_unlock(nip);
649                 hammer2_inode_chain_sync(nip);
650                 hammer2_inode_drop(nip);
651
652                 /* 
653                  * We still have a ref on the chain, relock and associate
654                  * with an appropriate PFS.
655                  */
656                 force_local = (hmp->hflags & HMNT2_LOCAL) ? hmp : NULL;
657
658                 hammer2_chain_lock(nchain, HAMMER2_RESOLVE_ALWAYS);
659                 nipdata = &nchain->data->ipdata;
660                 kprintf("ADD LOCAL PFS (IOCTL): %s\n", nipdata->filename);
661                 hammer2_pfsalloc(nchain, nipdata,
662                                  nchain->bref.modify_tid, force_local);
663
664                 hammer2_chain_unlock(nchain);
665                 hammer2_chain_drop(nchain);
666
667         }
668         hammer2_trans_done(hmp->spmp);
669
670         return (error);
671 }
672
673 /*
674  * Destroy an existing PFS under the super-root
675  */
676 static int
677 hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
678 {
679         hammer2_ioc_pfs_t *pfs = data;
680         hammer2_dev_t   *hmp;
681         hammer2_pfs_t   *spmp;
682         hammer2_pfs_t   *pmp;
683         hammer2_xop_unlink_t *xop;
684         hammer2_inode_t *dip;
685         hammer2_inode_t *iroot;
686         int error;
687         int i;
688
689         /*
690          * The PFS should be probed, so we should be able to
691          * locate it.  We only delete the PFS from the
692          * specific H2 block device (hmp), not all of
693          * them.  We must remove the PFS from the cluster
694          * before we can destroy it.
695          */
696         hmp = ip->pmp->pfs_hmps[0];
697         if (hmp == NULL)
698                 return (EINVAL);
699
700         pfs->name[sizeof(pfs->name) - 1] = 0;   /* ensure termination */
701
702         lockmgr(&hammer2_mntlk, LK_EXCLUSIVE);
703
704         TAILQ_FOREACH(pmp, &hammer2_pfslist, mntentry) {
705                 for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
706                         if (pmp->pfs_hmps[i] != hmp)
707                                 continue;
708                         if (pmp->pfs_names[i] &&
709                             strcmp(pmp->pfs_names[i], pfs->name) == 0) {
710                                 break;
711                         }
712                 }
713                 if (i != HAMMER2_MAXCLUSTER)
714                         break;
715         }
716
717         if (pmp == NULL) {
718                 lockmgr(&hammer2_mntlk, LK_RELEASE);
719                 return ENOENT;
720         }
721
722         /*
723          * Ok, we found the pmp and we have the index.  Permanently remove
724          * the PFS from the cluster
725          */
726         iroot = pmp->iroot;
727         kprintf("FOUND PFS %s CLINDEX %d\n", pfs->name, i);
728         hammer2_pfsdealloc(pmp, i, 1);
729
730         lockmgr(&hammer2_mntlk, LK_RELEASE);
731
732         /*
733          * Now destroy the PFS under its device using the per-device
734          * super-root.
735          */
736         spmp = hmp->spmp;
737         dip = spmp->iroot;
738         hammer2_trans_init(spmp, 0);
739         hammer2_inode_lock(dip, 0);
740
741         xop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING);
742         hammer2_xop_setname(&xop->head, pfs->name, strlen(pfs->name));
743         xop->isdir = 2;
744         xop->dopermanent = 2 | 1;       /* FORCE | PERMANENT */
745         hammer2_xop_start(&xop->head, hammer2_xop_unlink);
746
747         error = hammer2_xop_collect(&xop->head, 0);
748
749         hammer2_inode_unlock(dip);
750
751 #if 0
752         if (error == 0) {
753                 ip = hammer2_inode_get(dip->pmp, dip, &xop->head.cluster, -1);
754                 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
755                 if (ip) {
756                         hammer2_inode_unlink_finisher(ip, 0);
757                         hammer2_inode_unlock(ip);
758                 }
759         } else {
760                 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
761         }
762 #endif
763         hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
764
765         hammer2_trans_done(spmp);
766
767         return (error);
768 }
769
770 static int
771 hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data)
772 {
773         hammer2_ioc_pfs_t *pfs = data;
774         hammer2_dev_t   *hmp;
775         hammer2_pfs_t   *pmp;
776         hammer2_chain_t *chain;
777         hammer2_tid_t   mtid;
778         int error;
779
780         if (pfs->name[0] == 0)
781                 return(EINVAL);
782         if (pfs->name[sizeof(pfs->name)-1] != 0)
783                 return(EINVAL);
784
785         pmp = ip->pmp;
786         ip = pmp->iroot;
787
788         hmp = pmp->pfs_hmps[0];
789         if (hmp == NULL)
790                 return (EINVAL);
791
792         lockmgr(&hmp->bulklk, LK_EXCLUSIVE);
793
794         hammer2_vfs_sync(pmp->mp, MNT_WAIT);
795
796         hammer2_trans_init(pmp, HAMMER2_TRANS_ISFLUSH);
797         mtid = hammer2_trans_sub(pmp);
798         hammer2_inode_lock(ip, 0);
799         hammer2_inode_modify(ip);
800         ip->meta.pfs_lsnap_tid = mtid;
801
802         /* XXX cluster it! */
803         chain = hammer2_inode_chain(ip, 0, HAMMER2_RESOLVE_ALWAYS);
804         error = hammer2_chain_snapshot(chain, pfs, mtid);
805         hammer2_chain_unlock(chain);
806         hammer2_chain_drop(chain);
807
808         hammer2_inode_unlock(ip);
809         hammer2_trans_done(pmp);
810
811         lockmgr(&hmp->bulklk, LK_RELEASE);
812
813         return (error);
814 }
815
816 /*
817  * Retrieve the raw inode structure, non-inclusive of node-specific data.
818  */
819 static int
820 hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data)
821 {
822         hammer2_ioc_inode_t *ino;
823         hammer2_chain_t *chain;
824         int error;
825         int i;
826
827         ino = data;
828         error = 0;
829
830         hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
831         ino->data_count = 0;
832         ino->inode_count = 0;
833         for (i = 0; i < ip->cluster.nchains; ++i) {
834                 if ((chain = ip->cluster.array[i].chain) != NULL) {
835                         if (ino->data_count <
836                             chain->bref.embed.stats.data_count) {
837                                 ino->data_count =
838                                         chain->bref.embed.stats.data_count;
839                         }
840                         if (ino->inode_count <
841                             chain->bref.embed.stats.inode_count) {
842                                 ino->inode_count =
843                                         chain->bref.embed.stats.inode_count;
844                         }
845                 }
846         }
847         bzero(&ino->ip_data, sizeof(ino->ip_data));
848         ino->ip_data.meta = ip->meta;
849         ino->kdata = ip;
850         hammer2_inode_unlock(ip);
851
852         return error;
853 }
854
855 /*
856  * Set various parameters in an inode which cannot be set through
857  * normal filesystem VNOPS.
858  */
859 static int
860 hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
861 {
862         hammer2_ioc_inode_t *ino = data;
863         int error = 0;
864
865         hammer2_trans_init(ip->pmp, 0);
866         hammer2_inode_lock(ip, 0);
867
868         if ((ino->flags & HAMMER2IOC_INODE_FLAG_CHECK) &&
869             ip->meta.check_algo != ino->ip_data.meta.check_algo) {
870                 hammer2_inode_modify(ip);
871                 ip->meta.check_algo = ino->ip_data.meta.check_algo;
872         }
873         if ((ino->flags & HAMMER2IOC_INODE_FLAG_COMP) &&
874             ip->meta.comp_algo != ino->ip_data.meta.comp_algo) {
875                 hammer2_inode_modify(ip);
876                 ip->meta.comp_algo = ino->ip_data.meta.comp_algo;
877         }
878         ino->kdata = ip;
879         
880         /* Ignore these flags for now...*/
881         if ((ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) &&
882             ip->meta.inode_quota != ino->ip_data.meta.inode_quota) {
883                 hammer2_inode_modify(ip);
884                 ip->meta.inode_quota = ino->ip_data.meta.inode_quota;
885         }
886         if ((ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) &&
887             ip->meta.data_quota != ino->ip_data.meta.data_quota) {
888                 hammer2_inode_modify(ip);
889                 ip->meta.data_quota = ino->ip_data.meta.data_quota;
890         }
891         if ((ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) &&
892             ip->meta.ncopies != ino->ip_data.meta.ncopies) {
893                 hammer2_inode_modify(ip);
894                 ip->meta.ncopies = ino->ip_data.meta.ncopies;
895         }
896         hammer2_inode_unlock(ip);
897         hammer2_trans_done(ip->pmp);
898
899         return (error);
900 }
901
902 static
903 int
904 hammer2_ioctl_debug_dump(hammer2_inode_t *ip)
905 {
906         hammer2_chain_t *chain;
907         int count = 1000;
908         int i;
909
910         for (i = 0; i < ip->cluster.nchains; ++i) {
911                 chain = ip->cluster.array[i].chain;
912                 if (chain == NULL)
913                         continue;
914                 hammer2_dump_chain(chain, 0, &count, 'i');
915         }
916         return 0;
917 }
918
919 /*
920  * Executes one flush/free pass per call.  If trying to recover
921  * data we just freed up a moment ago it can take up to six passes
922  * to fully free the blocks.  Note that passes occur automatically based
923  * on free space as the storage fills up, but manual passes may be needed
924  * if storage becomes almost completely full.
925  */
926 static
927 int
928 hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data)
929 {
930         hammer2_ioc_bulkfree_t *bfi = data;
931         hammer2_dev_t   *hmp;
932         hammer2_pfs_t   *pmp;
933         hammer2_chain_t *vchain;
934         int error;
935
936         pmp = ip->pmp;
937         ip = pmp->iroot;
938
939         hmp = pmp->pfs_hmps[0];
940         if (hmp == NULL)
941                 return (EINVAL);
942         if (bfi == NULL)
943                 return (EINVAL);
944
945         /*
946          * Bulkfree has to be serialized to guarantee at least one sync
947          * inbetween bulkfrees.
948          */
949         error = lockmgr(&hmp->bflock, LK_EXCLUSIVE | LK_PCATCH);
950         if (error)
951                 return error;
952
953         /*
954          * sync the filesystem and obtain a snapshot of the synchronized
955          * hmp volume header.  We treat the snapshot as an independent
956          * entity.
957          */
958         hammer2_vfs_sync(pmp->mp, MNT_WAIT);
959         vchain = hammer2_chain_bulksnap(hmp);
960
961         /*
962          * Normal filesystem operations will not interfere with the
963          * synchronized block hierarchy and can run concurrent with the
964          * bulkfree pass.
965          */
966         hammer2_trans_init(pmp, 0);
967         if (bfi) {
968                 hammer2_thr_freeze(&hmp->bfthr);
969                 error = hammer2_bulkfree_pass(hmp, vchain, bfi);
970                 hammer2_thr_unfreeze(&hmp->bfthr);
971         }
972         hammer2_chain_bulkdrop(vchain);
973         hammer2_trans_done(pmp);
974
975         lockmgr(&hmp->bflock, LK_RELEASE);
976
977         return (hammer2_error_to_errno(error));
978 }