6dacdd2faef0b1e3f666c5dd43634f324811b50c
[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 cache_index = -1;
408         int error;
409
410         hmp = ip->pmp->pfs_hmps[0];
411         if (hmp == NULL)
412                 return (EINVAL);
413
414         pfs = data;
415         save_key = pfs->name_key;
416         error = 0;
417
418         /*
419          * Setup
420          */
421         if (save_key == (hammer2_key_t)-1) {
422                 hammer2_inode_lock(ip->pmp->iroot, 0);
423                 parent = NULL;
424                 chain = hammer2_inode_chain(ip->pmp->iroot, 0,
425                                             HAMMER2_RESOLVE_ALWAYS |
426                                             HAMMER2_RESOLVE_SHARED);
427         } else {
428                 hammer2_inode_lock(hmp->spmp->iroot, 0);
429                 parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
430                                             HAMMER2_RESOLVE_ALWAYS |
431                                             HAMMER2_RESOLVE_SHARED);
432                 chain = hammer2_chain_lookup(&parent, &key_next,
433                                             pfs->name_key, HAMMER2_KEY_MAX,
434                                             &cache_index,
435                                             HAMMER2_LOOKUP_SHARED);
436         }
437
438         /*
439          * Locate next PFS
440          */
441         while (chain) {
442                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE)
443                         break;
444                 if (parent == NULL) {
445                         hammer2_chain_unlock(chain);
446                         hammer2_chain_drop(chain);
447                         chain = NULL;
448                         break;
449                 }
450                 chain = hammer2_chain_next(&parent, chain, &key_next,
451                                             key_next, HAMMER2_KEY_MAX,
452                                             &cache_index,
453                                             HAMMER2_LOOKUP_SHARED);
454         }
455
456         /*
457          * Load the data being returned by the ioctl.
458          */
459         if (chain) {
460                 ripdata = &chain->data->ipdata;
461                 pfs->name_key = ripdata->meta.name_key;
462                 pfs->pfs_type = ripdata->meta.pfs_type;
463                 pfs->pfs_subtype = ripdata->meta.pfs_subtype;
464                 pfs->pfs_clid = ripdata->meta.pfs_clid;
465                 pfs->pfs_fsid = ripdata->meta.pfs_fsid;
466                 KKASSERT(ripdata->meta.name_len < sizeof(pfs->name));
467                 bcopy(ripdata->filename, pfs->name, ripdata->meta.name_len);
468                 pfs->name[ripdata->meta.name_len] = 0;
469                 ripdata = NULL; /* safety */
470
471                 /*
472                  * Calculate name_next, if any.
473                  */
474                 if (parent == NULL) {
475                         pfs->name_next = (hammer2_key_t)-1;
476                 } else {
477                         chain = hammer2_chain_next(&parent, chain, &key_next,
478                                                     key_next, HAMMER2_KEY_MAX,
479                                                     &cache_index,
480                                                     HAMMER2_LOOKUP_SHARED);
481                         if (chain)
482                                 pfs->name_next = chain->bref.key;
483                         else
484                                 pfs->name_next = (hammer2_key_t)-1;
485                 }
486         } else {
487                 pfs->name_next = (hammer2_key_t)-1;
488                 error = ENOENT;
489         }
490
491         /*
492          * Cleanup
493          */
494         if (chain) {
495                 hammer2_chain_unlock(chain);
496                 hammer2_chain_drop(chain);
497         }
498         if (parent) {
499                 hammer2_chain_unlock(parent);
500                 hammer2_chain_drop(parent);
501         }
502         if (save_key == (hammer2_key_t)-1) {
503                 hammer2_inode_unlock(ip->pmp->iroot);
504         } else {
505                 hammer2_inode_unlock(hmp->spmp->iroot);
506         }
507
508         return (error);
509 }
510
511 /*
512  * Find a specific PFS by name
513  */
514 static int
515 hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data)
516 {
517         const hammer2_inode_data_t *ripdata;
518         hammer2_dev_t *hmp;
519         hammer2_ioc_pfs_t *pfs;
520         hammer2_chain_t *parent;
521         hammer2_chain_t *chain;
522         hammer2_key_t key_next;
523         hammer2_key_t lhc;
524         int cache_index = -1;
525         int error;
526         size_t len;
527
528         hmp = ip->pmp->pfs_hmps[0];
529         if (hmp == NULL)
530                 return (EINVAL);
531
532         pfs = data;
533         error = 0;
534
535         hammer2_inode_lock(hmp->spmp->iroot, HAMMER2_RESOLVE_SHARED);
536         parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
537                                      HAMMER2_RESOLVE_ALWAYS |
538                                      HAMMER2_RESOLVE_SHARED);
539
540         pfs->name[sizeof(pfs->name) - 1] = 0;
541         len = strlen(pfs->name);
542         lhc = hammer2_dirhash(pfs->name, len);
543
544         chain = hammer2_chain_lookup(&parent, &key_next,
545                                          lhc, lhc + HAMMER2_DIRHASH_LOMASK,
546                                          &cache_index,
547                                          HAMMER2_LOOKUP_SHARED);
548         while (chain) {
549                 if (hammer2_chain_dirent_test(chain, pfs->name, len))
550                         break;
551                 chain = hammer2_chain_next(&parent, chain, &key_next,
552                                            key_next,
553                                            lhc + HAMMER2_DIRHASH_LOMASK,
554                                            &cache_index,
555                                            HAMMER2_LOOKUP_SHARED);
556         }
557
558         /*
559          * Load the data being returned by the ioctl.
560          */
561         if (chain) {
562                 KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
563                 ripdata = &chain->data->ipdata;
564                 pfs->name_key = ripdata->meta.name_key;
565                 pfs->pfs_type = ripdata->meta.pfs_type;
566                 pfs->pfs_subtype = ripdata->meta.pfs_subtype;
567                 pfs->pfs_clid = ripdata->meta.pfs_clid;
568                 pfs->pfs_fsid = ripdata->meta.pfs_fsid;
569                 ripdata = NULL;
570
571                 hammer2_chain_unlock(chain);
572                 hammer2_chain_drop(chain);
573         } else {
574                 error = ENOENT;
575         }
576         if (parent) {
577                 hammer2_chain_unlock(parent);
578                 hammer2_chain_drop(parent);
579         }
580         hammer2_inode_unlock(hmp->spmp->iroot);
581
582         return (error);
583 }
584
585 /*
586  * Create a new PFS under the super-root
587  */
588 static int
589 hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data)
590 {
591         hammer2_inode_data_t *nipdata;
592         hammer2_chain_t *nchain;
593         hammer2_dev_t *hmp;
594         hammer2_dev_t *force_local;
595         hammer2_ioc_pfs_t *pfs;
596         hammer2_inode_t *nip;
597         hammer2_tid_t mtid;
598         int error;
599
600         hmp = ip->pmp->pfs_hmps[0];     /* XXX */
601         if (hmp == NULL)
602                 return (EINVAL);
603
604         pfs = data;
605         nip = NULL;
606
607         if (pfs->name[0] == 0)
608                 return(EINVAL);
609         pfs->name[sizeof(pfs->name) - 1] = 0;   /* ensure 0-termination */
610
611         if (hammer2_ioctl_pfs_lookup(ip, pfs) == 0)
612                 return(EEXIST);
613
614         hammer2_trans_init(hmp->spmp, 0);
615         mtid = hammer2_trans_sub(hmp->spmp);
616         nip = hammer2_inode_create(hmp->spmp->iroot, hmp->spmp->iroot,
617                                    NULL, NULL,
618                                    pfs->name, strlen(pfs->name), 0,
619                                    1, HAMMER2_OBJTYPE_DIRECTORY, 0,
620                                    HAMMER2_INSERT_PFSROOT, &error);
621         if (error == 0) {
622                 hammer2_inode_modify(nip);
623                 nchain = hammer2_inode_chain(nip, 0, HAMMER2_RESOLVE_ALWAYS);
624                 hammer2_chain_modify(nchain, mtid, 0, 0);
625                 nipdata = &nchain->data->ipdata;
626
627                 nip->meta.pfs_type = pfs->pfs_type;
628                 nip->meta.pfs_subtype = pfs->pfs_subtype;
629                 nip->meta.pfs_clid = pfs->pfs_clid;
630                 nip->meta.pfs_fsid = pfs->pfs_fsid;
631                 nip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT;
632
633                 /*
634                  * Set default compression and check algorithm.  This
635                  * can be changed later.
636                  *
637                  * Do not allow compression on PFS's with the special name
638                  * "boot", the boot loader can't decompress (yet).
639                  */
640                 nip->meta.comp_algo =
641                         HAMMER2_ENC_ALGO(HAMMER2_COMP_NEWFS_DEFAULT);
642                 nip->meta.check_algo =
643                         HAMMER2_ENC_ALGO( HAMMER2_CHECK_XXHASH64);
644
645                 if (strcasecmp(pfs->name, "boot") == 0) {
646                         nip->meta.comp_algo =
647                                 HAMMER2_ENC_ALGO(HAMMER2_COMP_AUTOZERO);
648                 }
649
650                 /*
651                  * Super-root isn't mounted, fsync it
652                  */
653                 hammer2_chain_unlock(nchain);
654                 hammer2_inode_ref(nip);
655                 hammer2_inode_unlock(nip);
656                 hammer2_inode_chain_sync(nip);
657                 hammer2_inode_drop(nip);
658
659                 /* 
660                  * We still have a ref on the chain, relock and associate
661                  * with an appropriate PFS.
662                  */
663                 force_local = (hmp->hflags & HMNT2_LOCAL) ? hmp : NULL;
664
665                 hammer2_chain_lock(nchain, HAMMER2_RESOLVE_ALWAYS);
666                 nipdata = &nchain->data->ipdata;
667                 kprintf("ADD LOCAL PFS (IOCTL): %s\n", nipdata->filename);
668                 hammer2_pfsalloc(nchain, nipdata,
669                                  nchain->bref.modify_tid, force_local);
670
671                 hammer2_chain_unlock(nchain);
672                 hammer2_chain_drop(nchain);
673
674         }
675         hammer2_trans_done(hmp->spmp);
676
677         return (error);
678 }
679
680 /*
681  * Destroy an existing PFS under the super-root
682  */
683 static int
684 hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
685 {
686         hammer2_ioc_pfs_t *pfs = data;
687         hammer2_dev_t   *hmp;
688         hammer2_pfs_t   *spmp;
689         hammer2_pfs_t   *pmp;
690         hammer2_xop_unlink_t *xop;
691         hammer2_inode_t *dip;
692         hammer2_inode_t *iroot;
693         int error;
694         int i;
695
696         /*
697          * The PFS should be probed, so we should be able to
698          * locate it.  We only delete the PFS from the
699          * specific H2 block device (hmp), not all of
700          * them.  We must remove the PFS from the cluster
701          * before we can destroy it.
702          */
703         hmp = ip->pmp->pfs_hmps[0];
704         if (hmp == NULL)
705                 return (EINVAL);
706
707         pfs->name[sizeof(pfs->name) - 1] = 0;   /* ensure termination */
708
709         lockmgr(&hammer2_mntlk, LK_EXCLUSIVE);
710
711         TAILQ_FOREACH(pmp, &hammer2_pfslist, mntentry) {
712                 for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
713                         if (pmp->pfs_hmps[i] != hmp)
714                                 continue;
715                         if (pmp->pfs_names[i] &&
716                             strcmp(pmp->pfs_names[i], pfs->name) == 0) {
717                                 break;
718                         }
719                 }
720                 if (i != HAMMER2_MAXCLUSTER)
721                         break;
722         }
723
724         if (pmp == NULL) {
725                 lockmgr(&hammer2_mntlk, LK_RELEASE);
726                 return ENOENT;
727         }
728
729         /*
730          * Ok, we found the pmp and we have the index.  Permanently remove
731          * the PFS from the cluster
732          */
733         iroot = pmp->iroot;
734         kprintf("FOUND PFS %s CLINDEX %d\n", pfs->name, i);
735         hammer2_pfsdealloc(pmp, i, 1);
736
737         lockmgr(&hammer2_mntlk, LK_RELEASE);
738
739         /*
740          * Now destroy the PFS under its device using the per-device
741          * super-root.
742          */
743         spmp = hmp->spmp;
744         dip = spmp->iroot;
745         hammer2_trans_init(spmp, 0);
746         hammer2_inode_lock(dip, 0);
747
748         xop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING);
749         hammer2_xop_setname(&xop->head, pfs->name, strlen(pfs->name));
750         xop->isdir = 2;
751         xop->dopermanent = 2 | 1;       /* FORCE | PERMANENT */
752         hammer2_xop_start(&xop->head, hammer2_xop_unlink);
753
754         error = hammer2_xop_collect(&xop->head, 0);
755
756         hammer2_inode_unlock(dip);
757
758 #if 0
759         if (error == 0) {
760                 ip = hammer2_inode_get(dip->pmp, dip, &xop->head.cluster, -1);
761                 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
762                 if (ip) {
763                         hammer2_inode_unlink_finisher(ip, 0);
764                         hammer2_inode_unlock(ip);
765                 }
766         } else {
767                 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
768         }
769 #endif
770         hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
771
772         hammer2_trans_done(spmp);
773
774         return (error);
775 }
776
777 static int
778 hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data)
779 {
780         hammer2_ioc_pfs_t *pfs = data;
781         hammer2_dev_t   *hmp;
782         hammer2_pfs_t   *pmp;
783         hammer2_chain_t *chain;
784         hammer2_tid_t   mtid;
785         int error;
786
787         if (pfs->name[0] == 0)
788                 return(EINVAL);
789         if (pfs->name[sizeof(pfs->name)-1] != 0)
790                 return(EINVAL);
791
792         pmp = ip->pmp;
793         ip = pmp->iroot;
794
795         hmp = pmp->pfs_hmps[0];
796         if (hmp == NULL)
797                 return (EINVAL);
798
799         lockmgr(&hmp->bulklk, LK_EXCLUSIVE);
800
801         hammer2_vfs_sync(pmp->mp, MNT_WAIT);
802
803         hammer2_trans_init(pmp, HAMMER2_TRANS_ISFLUSH);
804         mtid = hammer2_trans_sub(pmp);
805         hammer2_inode_lock(ip, 0);
806         hammer2_inode_modify(ip);
807         ip->meta.pfs_lsnap_tid = mtid;
808
809         /* XXX cluster it! */
810         chain = hammer2_inode_chain(ip, 0, HAMMER2_RESOLVE_ALWAYS);
811         error = hammer2_chain_snapshot(chain, pfs, mtid);
812         hammer2_chain_unlock(chain);
813         hammer2_chain_drop(chain);
814
815         hammer2_inode_unlock(ip);
816         hammer2_trans_done(pmp);
817
818         lockmgr(&hmp->bulklk, LK_RELEASE);
819
820         return (error);
821 }
822
823 /*
824  * Retrieve the raw inode structure, non-inclusive of node-specific data.
825  */
826 static int
827 hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data)
828 {
829         hammer2_ioc_inode_t *ino;
830         hammer2_chain_t *chain;
831         int error;
832         int i;
833
834         ino = data;
835         error = 0;
836
837         hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
838         ino->data_count = 0;
839         ino->inode_count = 0;
840         for (i = 0; i < ip->cluster.nchains; ++i) {
841                 if ((chain = ip->cluster.array[i].chain) != NULL) {
842                         if (ino->data_count <
843                             chain->bref.embed.stats.data_count) {
844                                 ino->data_count =
845                                         chain->bref.embed.stats.data_count;
846                         }
847                         if (ino->inode_count <
848                             chain->bref.embed.stats.inode_count) {
849                                 ino->inode_count =
850                                         chain->bref.embed.stats.inode_count;
851                         }
852                 }
853         }
854         bzero(&ino->ip_data, sizeof(ino->ip_data));
855         ino->ip_data.meta = ip->meta;
856         ino->kdata = ip;
857         hammer2_inode_unlock(ip);
858
859         return error;
860 }
861
862 /*
863  * Set various parameters in an inode which cannot be set through
864  * normal filesystem VNOPS.
865  */
866 static int
867 hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
868 {
869         hammer2_ioc_inode_t *ino = data;
870         int error = 0;
871
872         hammer2_trans_init(ip->pmp, 0);
873         hammer2_inode_lock(ip, 0);
874
875         if ((ino->flags & HAMMER2IOC_INODE_FLAG_CHECK) &&
876             ip->meta.check_algo != ino->ip_data.meta.check_algo) {
877                 hammer2_inode_modify(ip);
878                 ip->meta.check_algo = ino->ip_data.meta.check_algo;
879         }
880         if ((ino->flags & HAMMER2IOC_INODE_FLAG_COMP) &&
881             ip->meta.comp_algo != ino->ip_data.meta.comp_algo) {
882                 hammer2_inode_modify(ip);
883                 ip->meta.comp_algo = ino->ip_data.meta.comp_algo;
884         }
885         ino->kdata = ip;
886         
887         /* Ignore these flags for now...*/
888         if ((ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) &&
889             ip->meta.inode_quota != ino->ip_data.meta.inode_quota) {
890                 hammer2_inode_modify(ip);
891                 ip->meta.inode_quota = ino->ip_data.meta.inode_quota;
892         }
893         if ((ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) &&
894             ip->meta.data_quota != ino->ip_data.meta.data_quota) {
895                 hammer2_inode_modify(ip);
896                 ip->meta.data_quota = ino->ip_data.meta.data_quota;
897         }
898         if ((ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) &&
899             ip->meta.ncopies != ino->ip_data.meta.ncopies) {
900                 hammer2_inode_modify(ip);
901                 ip->meta.ncopies = ino->ip_data.meta.ncopies;
902         }
903         hammer2_inode_unlock(ip);
904         hammer2_trans_done(ip->pmp);
905
906         return (error);
907 }
908
909 static
910 int
911 hammer2_ioctl_debug_dump(hammer2_inode_t *ip)
912 {
913         hammer2_chain_t *chain;
914         int count = 1000;
915         int i;
916
917         for (i = 0; i < ip->cluster.nchains; ++i) {
918                 chain = ip->cluster.array[i].chain;
919                 if (chain == NULL)
920                         continue;
921                 hammer2_dump_chain(chain, 0, &count, 'i');
922         }
923         return 0;
924 }
925
926 /*
927  * Executes one flush/free pass per call.  If trying to recover
928  * data we just freed up a moment ago it can take up to six passes
929  * to fully free the blocks.  Note that passes occur automatically based
930  * on free space as the storage fills up, but manual passes may be needed
931  * if storage becomes almost completely full.
932  */
933 static
934 int
935 hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data)
936 {
937         hammer2_ioc_bulkfree_t *bfi = data;
938         hammer2_dev_t   *hmp;
939         hammer2_pfs_t   *pmp;
940         hammer2_chain_t *vchain;
941         int error;
942
943         pmp = ip->pmp;
944         ip = pmp->iroot;
945
946         hmp = pmp->pfs_hmps[0];
947         if (hmp == NULL)
948                 return (EINVAL);
949         if (bfi == NULL)
950                 return (EINVAL);
951
952         /*
953          * Bulkfree has to be serialized to guarantee at least one sync
954          * inbetween bulkfrees.
955          */
956         error = lockmgr(&hmp->bflock, LK_EXCLUSIVE | LK_PCATCH);
957         if (error)
958                 return error;
959
960         /*
961          * sync the filesystem and obtain a snapshot of the synchronized
962          * hmp volume header.  We treat the snapshot as an independent
963          * entity.
964          */
965         hammer2_vfs_sync(pmp->mp, MNT_WAIT);
966         vchain = hammer2_chain_bulksnap(hmp);
967
968         /*
969          * Normal filesystem operations will not interfere with the
970          * synchronized block hierarchy and can run concurrent with the
971          * bulkfree pass.
972          */
973         hammer2_trans_init(pmp, 0);
974         if (bfi) {
975                 hammer2_thr_freeze(&hmp->bfthr);
976                 error = hammer2_bulkfree_pass(hmp, vchain, bfi);
977                 hammer2_thr_unfreeze(&hmp->bfthr);
978         }
979         hammer2_chain_bulkdrop(vchain);
980         hammer2_trans_done(pmp);
981
982         lockmgr(&hmp->bflock, LK_RELEASE);
983
984         return (error);
985 }