hammer2 - Refactor frontend part 13/many
[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_INODE_COMP_SET:
141                 error = hammer2_ioctl_inode_comp_set(ip, data);
142                 break;
143         case HAMMER2IOC_INODE_COMP_REC_SET:
144                 error = hammer2_ioctl_inode_comp_rec_set(ip, data);
145                 break;
146         case HAMMER2IOC_INODE_COMP_REC_SET2:
147                 error = hammer2_ioctl_inode_comp_rec_set2(ip, data);
148                 break;*/
149         case HAMMER2IOC_DEBUG_DUMP:
150                 error = hammer2_ioctl_debug_dump(ip);
151                 break;
152         default:
153                 error = EOPNOTSUPP;
154                 break;
155         }
156         return (error);
157 }
158
159 /*
160  * Retrieve version and basic info
161  */
162 static int
163 hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data)
164 {
165         hammer2_dev_t *hmp = ip->pmp->iroot->cluster.focus->hmp;
166         hammer2_ioc_version_t *version = data;
167
168         version->version = hmp->voldata.version;
169         return 0;
170 }
171
172 static int
173 hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data)
174 {
175         hammer2_ioc_recluster_t *recl = data;
176         struct file *fp;
177         hammer2_cluster_t *cluster;
178         int error;
179
180         fp = holdfp(curproc->p_fd, recl->fd, -1);
181         if (fp) {
182                 kprintf("reconnect to cluster: XXX ");
183                 cluster = &ip->pmp->iroot->cluster;
184                 if (cluster->nchains != 1 || cluster->focus == NULL) {
185                         kprintf("not a local device mount\n");
186                         error = EINVAL;
187                 } else {
188                         hammer2_cluster_reconnect(cluster->focus->hmp, fp);
189                         kprintf("ok\n");
190                         error = 0;
191                 }
192         } else {
193                 error = EINVAL;
194         }
195         return error;
196 }
197
198 /*
199  * Retrieve information about a remote
200  */
201 static int
202 hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data)
203 {
204         hammer2_dev_t *hmp = ip->pmp->iroot->cluster.focus->hmp;
205         hammer2_ioc_remote_t *remote = data;
206         int copyid = remote->copyid;
207
208         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
209                 return (EINVAL);
210
211         hammer2_voldata_lock(hmp);
212         remote->copy1 = hmp->voldata.copyinfo[copyid];
213         hammer2_voldata_unlock(hmp);
214
215         /*
216          * Adjust nextid (GET only)
217          */
218         while (++copyid < HAMMER2_COPYID_COUNT &&
219                hmp->voldata.copyinfo[copyid].copyid == 0) {
220                 ;
221         }
222         if (copyid == HAMMER2_COPYID_COUNT)
223                 remote->nextid = -1;
224         else
225                 remote->nextid = copyid;
226
227         return(0);
228 }
229
230 /*
231  * Add new remote entry
232  */
233 static int
234 hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data)
235 {
236         hammer2_ioc_remote_t *remote = data;
237         hammer2_pfs_t *pmp = ip->pmp;
238         hammer2_dev_t *hmp;
239         int copyid = remote->copyid;
240         int error = 0;
241
242         if (copyid >= HAMMER2_COPYID_COUNT)
243                 return (EINVAL);
244
245         hmp = pmp->iroot->cluster.focus->hmp; /* XXX */
246         hammer2_voldata_lock(hmp);
247         if (copyid < 0) {
248                 for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
249                         if (hmp->voldata.copyinfo[copyid].copyid == 0)
250                                 break;
251                 }
252                 if (copyid == HAMMER2_COPYID_COUNT) {
253                         error = ENOSPC;
254                         goto failed;
255                 }
256         }
257         hammer2_voldata_modify(hmp);
258         remote->copy1.copyid = copyid;
259         hmp->voldata.copyinfo[copyid] = remote->copy1;
260         hammer2_volconf_update(hmp, copyid);
261 failed:
262         hammer2_voldata_unlock(hmp);
263         return (error);
264 }
265
266 /*
267  * Delete existing remote entry
268  */
269 static int
270 hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data)
271 {
272         hammer2_ioc_remote_t *remote = data;
273         hammer2_pfs_t *pmp = ip->pmp;
274         hammer2_dev_t *hmp;
275         int copyid = remote->copyid;
276         int error = 0;
277
278         hmp = pmp->iroot->cluster.focus->hmp; /* XXX */
279         if (copyid >= HAMMER2_COPYID_COUNT)
280                 return (EINVAL);
281         remote->copy1.path[sizeof(remote->copy1.path) - 1] = 0;
282         hammer2_voldata_lock(hmp);
283         if (copyid < 0) {
284                 for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
285                         if (hmp->voldata.copyinfo[copyid].copyid == 0)
286                                 continue;
287                         if (strcmp(remote->copy1.path,
288                             hmp->voldata.copyinfo[copyid].path) == 0) {
289                                 break;
290                         }
291                 }
292                 if (copyid == HAMMER2_COPYID_COUNT) {
293                         error = ENOENT;
294                         goto failed;
295                 }
296         }
297         hammer2_voldata_modify(hmp);
298         hmp->voldata.copyinfo[copyid].copyid = 0;
299         hammer2_volconf_update(hmp, copyid);
300 failed:
301         hammer2_voldata_unlock(hmp);
302         return (error);
303 }
304
305 /*
306  * Replace existing remote entry
307  */
308 static int
309 hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data)
310 {
311         hammer2_ioc_remote_t *remote = data;
312         hammer2_dev_t *hmp;
313         int copyid = remote->copyid;
314
315         hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
316
317         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
318                 return (EINVAL);
319
320         hammer2_voldata_lock(hmp);
321         hammer2_voldata_modify(hmp);
322         /*hammer2_volconf_update(hmp, copyid);*/
323         hammer2_voldata_unlock(hmp);
324
325         return(0);
326 }
327
328 /*
329  * Retrieve communications socket
330  */
331 static int
332 hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data)
333 {
334         return (EOPNOTSUPP);
335 }
336
337 /*
338  * Set communications socket for connection
339  */
340 static int
341 hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data)
342 {
343         hammer2_ioc_remote_t *remote = data;
344         hammer2_dev_t *hmp;
345         int copyid = remote->copyid;
346
347         hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
348         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
349                 return (EINVAL);
350
351         hammer2_voldata_lock(hmp);
352         hammer2_voldata_unlock(hmp);
353
354         return(0);
355 }
356
357 /*
358  * Used to scan and retrieve PFS information.  PFS's are directories under
359  * the super-root.
360  *
361  * To scan PFSs pass name_key=0.  The function will scan for the next
362  * PFS and set all fields, as well as set name_next to the next key.
363  * When no PFSs remain, name_next is set to (hammer2_key_t)-1.
364  *
365  * To retrieve the PFS associated with the file descriptor, pass
366  * name_key set to (hammer2_key_t)-1.
367  */
368 static int
369 hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data)
370 {
371         const hammer2_inode_data_t *ripdata;
372         hammer2_dev_t *hmp;
373         hammer2_ioc_pfs_t *pfs;
374         hammer2_cluster_t *cparent;
375         hammer2_cluster_t *rcluster;
376         hammer2_cluster_t *cluster;
377         hammer2_key_t key_next;
378         int error;
379
380         error = 0;
381         hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
382         pfs = data;
383         hammer2_inode_lock(hmp->spmp->iroot, HAMMER2_RESOLVE_ALWAYS);
384         cparent = hammer2_inode_cluster(hmp->spmp->iroot,
385                                         HAMMER2_RESOLVE_ALWAYS);
386         hammer2_inode_lock(ip->pmp->iroot, HAMMER2_RESOLVE_ALWAYS);
387         rcluster = hammer2_inode_cluster(ip->pmp->iroot,
388                                          HAMMER2_RESOLVE_ALWAYS);
389
390         /*
391          * Search for the first key or specific key.  Remember that keys
392          * can be returned in any order.
393          */
394         if (pfs->name_key == 0) {
395                 cluster = hammer2_cluster_lookup(cparent, &key_next,
396                                                  0, (hammer2_key_t)-1,
397                                                  0);
398         } else if (pfs->name_key == (hammer2_key_t)-1) {
399                 ripdata = &hammer2_cluster_rdata(rcluster)->ipdata;
400                 cluster = hammer2_cluster_lookup(cparent, &key_next,
401                                                  ripdata->meta.name_key,
402                                                  ripdata->meta.name_key,
403                                                  0);
404                 ripdata = NULL; /* safety */
405         } else {
406                 cluster = hammer2_cluster_lookup(cparent, &key_next,
407                                                  pfs->name_key, pfs->name_key,
408                                                  0);
409         }
410         hammer2_inode_unlock(ip->pmp->iroot, rcluster);
411
412         while (cluster &&
413                hammer2_cluster_type(cluster) != HAMMER2_BREF_TYPE_INODE) {
414                 cluster = hammer2_cluster_next(cparent, cluster, &key_next,
415                                                key_next, (hammer2_key_t)-1,
416                                                0);
417         }
418         if (cluster) {
419                 /*
420                  * Load the data being returned by the ioctl.
421                  */
422                 ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
423                 pfs->name_key = ripdata->meta.name_key;
424                 pfs->pfs_type = ripdata->meta.pfs_type;
425                 pfs->pfs_subtype = ripdata->meta.pfs_subtype;
426                 pfs->pfs_clid = ripdata->meta.pfs_clid;
427                 pfs->pfs_fsid = ripdata->meta.pfs_fsid;
428                 KKASSERT(ripdata->meta.name_len < sizeof(pfs->name));
429                 bcopy(ripdata->filename, pfs->name, ripdata->meta.name_len);
430                 pfs->name[ripdata->meta.name_len] = 0;
431                 ripdata = NULL; /* safety */
432
433                 /*
434                  * Calculate the next field
435                  */
436                 do {
437                         cluster = hammer2_cluster_next(cparent, cluster,
438                                                        &key_next,
439                                                        0, (hammer2_key_t)-1,
440                                                        0);
441                 } while (cluster &&
442                          hammer2_cluster_type(cluster) !=
443                           HAMMER2_BREF_TYPE_INODE);
444                 if (cluster) {
445                         ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
446                         pfs->name_next = ripdata->meta.name_key;
447                         hammer2_cluster_unlock(cluster);
448                         hammer2_cluster_drop(cluster);
449                 } else {
450                         pfs->name_next = (hammer2_key_t)-1;
451                 }
452         } else {
453                 pfs->name_next = (hammer2_key_t)-1;
454                 error = ENOENT;
455         }
456         hammer2_inode_unlock(hmp->spmp->iroot, cparent);
457
458         return (error);
459 }
460
461 /*
462  * Find a specific PFS by name
463  */
464 static int
465 hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data)
466 {
467         const hammer2_inode_data_t *ripdata;
468         hammer2_dev_t *hmp;
469         hammer2_ioc_pfs_t *pfs;
470         hammer2_cluster_t *cparent;
471         hammer2_cluster_t *cluster;
472         hammer2_key_t key_next;
473         hammer2_key_t lhc;
474         int error;
475         size_t len;
476
477         error = 0;
478         hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
479         pfs = data;
480         hammer2_inode_lock(hmp->spmp->iroot, HAMMER2_RESOLVE_ALWAYS |
481                                              HAMMER2_RESOLVE_SHARED);
482         cparent = hammer2_inode_cluster(hmp->spmp->iroot,
483                                         HAMMER2_RESOLVE_ALWAYS |
484                                         HAMMER2_RESOLVE_SHARED);
485
486         pfs->name[sizeof(pfs->name) - 1] = 0;
487         len = strlen(pfs->name);
488         lhc = hammer2_dirhash(pfs->name, len);
489
490         cluster = hammer2_cluster_lookup(cparent, &key_next,
491                                          lhc, lhc + HAMMER2_DIRHASH_LOMASK,
492                                          HAMMER2_LOOKUP_SHARED);
493         while (cluster) {
494                 if (hammer2_cluster_type(cluster) == HAMMER2_BREF_TYPE_INODE) {
495                         ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
496                         if (ripdata->meta.name_len == len &&
497                             bcmp(ripdata->filename, pfs->name, len) == 0) {
498                                 break;
499                         }
500                         ripdata = NULL; /* safety */
501                 }
502                 cluster = hammer2_cluster_next(cparent, cluster, &key_next,
503                                            key_next,
504                                            lhc + HAMMER2_DIRHASH_LOMASK,
505                                            HAMMER2_LOOKUP_SHARED);
506         }
507
508         /*
509          * Load the data being returned by the ioctl.
510          */
511         if (cluster) {
512                 ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
513                 pfs->name_key = ripdata->meta.name_key;
514                 pfs->pfs_type = ripdata->meta.pfs_type;
515                 pfs->pfs_subtype = ripdata->meta.pfs_subtype;
516                 pfs->pfs_clid = ripdata->meta.pfs_clid;
517                 pfs->pfs_fsid = ripdata->meta.pfs_fsid;
518                 ripdata = NULL;
519
520                 hammer2_cluster_unlock(cluster);
521                 hammer2_cluster_drop(cluster);
522         } else {
523                 error = ENOENT;
524         }
525         hammer2_inode_unlock(hmp->spmp->iroot, cparent);
526
527         return (error);
528 }
529
530 /*
531  * Create a new PFS under the super-root
532  */
533 static int
534 hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data)
535 {
536         hammer2_inode_data_t *nipdata;
537         hammer2_chain_t *nchain;
538         hammer2_dev_t *hmp;
539         hammer2_ioc_pfs_t *pfs;
540         hammer2_inode_t *nip;
541         int error;
542
543         hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
544         pfs = data;
545         nip = NULL;
546
547         if (pfs->name[0] == 0)
548                 return(EINVAL);
549         pfs->name[sizeof(pfs->name) - 1] = 0;   /* ensure 0-termination */
550
551         if (hammer2_ioctl_pfs_lookup(ip, pfs) == 0)
552                 return(EEXIST);
553
554         hammer2_trans_init(hmp->spmp, 0);
555         nip = hammer2_inode_create(hmp->spmp->iroot, NULL, NULL,
556                                    pfs->name, strlen(pfs->name),
557                                    HAMMER2_INSERT_PFSROOT, &error);
558         if (error == 0) {
559                 hammer2_inode_modify(nip);
560                 nchain = hammer2_inode_chain(nip, 0, HAMMER2_RESOLVE_ALWAYS);
561                 hammer2_chain_modify(nchain, 0);
562                 nipdata = &nchain->data->ipdata;
563
564                 nip->meta.pfs_type = pfs->pfs_type;
565                 nip->meta.pfs_subtype = pfs->pfs_subtype;
566                 nip->meta.pfs_clid = pfs->pfs_clid;
567                 nip->meta.pfs_fsid = pfs->pfs_fsid;
568                 nip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT;
569
570                 /*
571                  * Set default compression and check algorithm.  This
572                  * can be changed later.
573                  *
574                  * Do not allow compression on PFS's with the special name
575                  * "boot", the boot loader can't decompress (yet).
576                  */
577                 nip->meta.comp_algo =
578                         HAMMER2_ENC_ALGO(HAMMER2_COMP_NEWFS_DEFAULT);
579                 nip->meta.check_algo =
580                         HAMMER2_ENC_ALGO( HAMMER2_CHECK_ISCSI32);
581
582                 if (strcasecmp(pfs->name, "boot") == 0) {
583                         nip->meta.comp_algo =
584                                 HAMMER2_ENC_ALGO(HAMMER2_COMP_AUTOZERO);
585                 }
586 #if 0
587                 hammer2_blockref_t bref;
588                 /* XXX new PFS needs to be rescanned / added */
589                 bref = nchain->bref;
590                 kprintf("ADD LOCAL PFS (IOCTL): %s\n", nipdata->filename);
591                 hammer2_pfsalloc(ncluster, nipdata, bref.modify_tid);
592 #endif
593                 /* XXX rescan */
594                 hammer2_chain_unlock(nchain);
595                 hammer2_chain_drop(nchain);
596
597                 hammer2_inode_unlock(nip, NULL);
598         }
599         hammer2_trans_done(hmp->spmp);
600
601         return (error);
602 }
603
604 /*
605  * Destroy an existing PFS under the super-root
606  */
607 static int
608 hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
609 {
610         hammer2_dev_t *hmp;
611         hammer2_ioc_pfs_t *pfs = data;
612         int error;
613
614         hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
615         hammer2_trans_init(hmp->spmp, 0);
616         error = hammer2_unlink_file(hmp->spmp->iroot, NULL,
617                                     pfs->name, strlen(pfs->name),
618                                     2, NULL, 0, 0);
619         hammer2_trans_done(hmp->spmp);
620
621         return (error);
622 }
623
624 static int
625 hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data)
626 {
627         hammer2_ioc_pfs_t *pfs = data;
628         hammer2_cluster_t *cparent;
629         int error;
630
631         if (pfs->name[0] == 0)
632                 return(EINVAL);
633         if (pfs->name[sizeof(pfs->name)-1] != 0)
634                 return(EINVAL);
635
636         hammer2_vfs_sync(ip->pmp->mp, MNT_WAIT);
637
638         hammer2_trans_init(ip->pmp, HAMMER2_TRANS_ISFLUSH);
639         hammer2_inode_lock(ip, HAMMER2_RESOLVE_ALWAYS);
640         cparent = hammer2_inode_cluster(ip, HAMMER2_RESOLVE_ALWAYS);
641         error = hammer2_cluster_snapshot(cparent, pfs);
642         hammer2_inode_unlock(ip, cparent);
643         hammer2_trans_done(ip->pmp);
644
645         return (error);
646 }
647
648 /*
649  * Retrieve the raw inode structure
650  */
651 static int
652 hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data)
653 {
654         const hammer2_inode_data_t *ripdata;
655         hammer2_ioc_inode_t *ino;
656         hammer2_cluster_t *cluster;
657         int error;
658
659         ino = data;
660
661         hammer2_inode_lock(ip, HAMMER2_RESOLVE_ALWAYS |
662                                HAMMER2_RESOLVE_SHARED);
663         cluster = hammer2_inode_cluster(ip, HAMMER2_RESOLVE_ALWAYS |
664                                             HAMMER2_RESOLVE_SHARED);
665         if (cluster->error) {
666                 error = EIO;
667         } else {
668                 ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
669                 ino->ip_data = *ripdata;
670                 ino->kdata = ip;
671                 ino->data_count = cluster->focus->bref.data_count;
672                 ino->inode_count = cluster->focus->bref.inode_count;
673                 error = 0;
674         }
675         hammer2_inode_unlock(ip, cluster);
676
677         return error;
678 }
679
680 /*
681  * Set various parameters in an inode which cannot be set through
682  * normal filesystem VNOPS.
683  */
684 static int
685 hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
686 {
687         const hammer2_inode_data_t *ripdata;
688         hammer2_inode_data_t *wipdata;
689         hammer2_ioc_inode_t *ino = data;
690         hammer2_cluster_t *cparent;
691         int error = 0;
692         int dosync = 0;
693
694         hammer2_trans_init(ip->pmp, 0);
695         hammer2_inode_lock(ip, HAMMER2_RESOLVE_ALWAYS);
696         cparent = hammer2_inode_cluster(ip, HAMMER2_RESOLVE_ALWAYS);
697         ripdata = &hammer2_cluster_rdata(cparent)->ipdata;
698
699         if (ino->ip_data.meta.check_algo != ripdata->meta.check_algo) {
700                 wipdata = hammer2_cluster_modify_ip(ip, cparent, 0);
701                 wipdata->meta.check_algo = ino->ip_data.meta.check_algo;
702                 ripdata = wipdata; /* safety */
703                 hammer2_cluster_setmethod_check(cparent,
704                                                 wipdata->meta.check_algo);
705                 dosync = 1;
706         }
707         if (ino->ip_data.meta.comp_algo != ripdata->meta.comp_algo) {
708                 wipdata = hammer2_cluster_modify_ip(ip, cparent, 0);
709                 wipdata->meta.comp_algo = ino->ip_data.meta.comp_algo;
710                 ripdata = wipdata; /* safety */
711                 dosync = 1;
712         }
713         ino->kdata = ip;
714         
715         /* Ignore these flags for now...*/
716         if (ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) {
717         }
718         if (ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) {
719         }
720         if (ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) {
721         }
722         if (dosync)
723                 hammer2_cluster_modsync(cparent);
724         hammer2_trans_done(ip->pmp);
725         hammer2_inode_unlock(ip, cparent);
726
727         return (error);
728 }
729
730 static
731 int
732 hammer2_ioctl_debug_dump(hammer2_inode_t *ip)
733 {
734         hammer2_chain_t *chain;
735         int count = 1000;
736         int i;
737
738         for (i = 0; i < ip->cluster.nchains; ++i) {
739                 chain = ip->cluster.array[i].chain;
740                 if (chain == NULL)
741                         continue;
742                 hammer2_dump_chain(chain, 0, &count, 'i');
743         }
744         return 0;
745 }
746
747 static
748 int
749 hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data)
750 {
751         hammer2_ioc_bulkfree_t *bfi = data;
752         hammer2_dev_t *hmp = ip->pmp->iroot->cluster.focus->hmp;
753         int error;
754
755         /* XXX run local cluster targets only */
756         error = hammer2_bulkfree_pass(hmp, bfi);
757
758         return error;
759 }