d84be942cf059bacb97dbe822b8621fc18475350
[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_mount_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_mount_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_pfsmount_t *pmp = ip->pmp;
238         hammer2_mount_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_pfsmount_t *pmp = ip->pmp;
274         hammer2_mount_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_mount_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_mount_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_mount_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         int ddflag;
380
381         error = 0;
382         hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
383         pfs = data;
384         cparent = hammer2_inode_lock_ex(hmp->spmp->iroot);
385         rcluster = hammer2_inode_lock_ex(ip->pmp->iroot);
386
387         /*
388          * Search for the first key or specific key.  Remember that keys
389          * can be returned in any order.
390          */
391         if (pfs->name_key == 0) {
392                 cluster = hammer2_cluster_lookup(cparent, &key_next,
393                                                  0, (hammer2_key_t)-1,
394                                                  0, &ddflag);
395         } else if (pfs->name_key == (hammer2_key_t)-1) {
396                 ripdata = &hammer2_cluster_rdata(rcluster)->ipdata;
397                 cluster = hammer2_cluster_lookup(cparent, &key_next,
398                                                  ripdata->name_key,
399                                                  ripdata->name_key,
400                                                  0, &ddflag);
401                 ripdata = NULL; /* safety */
402         } else {
403                 cluster = hammer2_cluster_lookup(cparent, &key_next,
404                                                  pfs->name_key, pfs->name_key,
405                                                  0, &ddflag);
406         }
407         hammer2_inode_unlock_ex(ip->pmp->iroot, rcluster);
408
409         while (cluster &&
410                hammer2_cluster_type(cluster) != HAMMER2_BREF_TYPE_INODE) {
411                 cluster = hammer2_cluster_next(cparent, cluster, &key_next,
412                                                key_next, (hammer2_key_t)-1,
413                                                0);
414         }
415         if (cluster) {
416                 /*
417                  * Load the data being returned by the ioctl.
418                  */
419                 ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
420                 pfs->name_key = ripdata->name_key;
421                 pfs->pfs_type = ripdata->pfs_type;
422                 pfs->pfs_clid = ripdata->pfs_clid;
423                 pfs->pfs_fsid = ripdata->pfs_fsid;
424                 KKASSERT(ripdata->name_len < sizeof(pfs->name));
425                 bcopy(ripdata->filename, pfs->name, ripdata->name_len);
426                 pfs->name[ripdata->name_len] = 0;
427                 ripdata = NULL; /* safety */
428
429                 /*
430                  * Calculate the next field
431                  */
432                 do {
433                         cluster = hammer2_cluster_next(cparent, cluster,
434                                                        &key_next,
435                                                        0, (hammer2_key_t)-1,
436                                                        0);
437                 } while (cluster &&
438                          hammer2_cluster_type(cluster) !=
439                           HAMMER2_BREF_TYPE_INODE);
440                 if (cluster) {
441                         ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
442                         pfs->name_next = ripdata->name_key;
443                         hammer2_cluster_unlock(cluster);
444                 } else {
445                         pfs->name_next = (hammer2_key_t)-1;
446                 }
447         } else {
448                 pfs->name_next = (hammer2_key_t)-1;
449                 error = ENOENT;
450         }
451         hammer2_inode_unlock_ex(hmp->spmp->iroot, cparent);
452
453         return (error);
454 }
455
456 /*
457  * Find a specific PFS by name
458  */
459 static int
460 hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data)
461 {
462         const hammer2_inode_data_t *ripdata;
463         hammer2_mount_t *hmp;
464         hammer2_ioc_pfs_t *pfs;
465         hammer2_cluster_t *cparent;
466         hammer2_cluster_t *cluster;
467         hammer2_key_t key_next;
468         hammer2_key_t lhc;
469         int error;
470         int ddflag;
471         size_t len;
472
473         error = 0;
474         hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
475         pfs = data;
476         cparent = hammer2_inode_lock_sh(hmp->spmp->iroot);
477
478         pfs->name[sizeof(pfs->name) - 1] = 0;
479         len = strlen(pfs->name);
480         lhc = hammer2_dirhash(pfs->name, len);
481
482         cluster = hammer2_cluster_lookup(cparent, &key_next,
483                                          lhc, lhc + HAMMER2_DIRHASH_LOMASK,
484                                          HAMMER2_LOOKUP_SHARED, &ddflag);
485         while (cluster) {
486                 if (hammer2_cluster_type(cluster) == HAMMER2_BREF_TYPE_INODE) {
487                         ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
488                         if (ripdata->name_len == len &&
489                             bcmp(ripdata->filename, pfs->name, len) == 0) {
490                                 break;
491                         }
492                         ripdata = NULL; /* safety */
493                 }
494                 cluster = hammer2_cluster_next(cparent, cluster, &key_next,
495                                            key_next,
496                                            lhc + HAMMER2_DIRHASH_LOMASK,
497                                            HAMMER2_LOOKUP_SHARED);
498         }
499
500         /*
501          * Load the data being returned by the ioctl.
502          */
503         if (cluster) {
504                 ripdata = &hammer2_cluster_rdata(cluster)->ipdata;
505                 pfs->name_key = ripdata->name_key;
506                 pfs->pfs_type = ripdata->pfs_type;
507                 pfs->pfs_clid = ripdata->pfs_clid;
508                 pfs->pfs_fsid = ripdata->pfs_fsid;
509                 ripdata = NULL;
510
511                 hammer2_cluster_unlock(cluster);
512         } else {
513                 error = ENOENT;
514         }
515         hammer2_inode_unlock_sh(hmp->spmp->iroot, cparent);
516
517         return (error);
518 }
519
520 /*
521  * Create a new PFS under the super-root
522  */
523 static int
524 hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data)
525 {
526         hammer2_inode_data_t *nipdata;
527         hammer2_mount_t *hmp;
528         hammer2_ioc_pfs_t *pfs;
529         hammer2_inode_t *nip;
530         hammer2_cluster_t *ncluster;
531         hammer2_trans_t trans;
532         int error;
533
534         hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
535         pfs = data;
536         nip = NULL;
537
538         if (pfs->name[0] == 0)
539                 return(EINVAL);
540         pfs->name[sizeof(pfs->name) - 1] = 0;   /* ensure 0-termination */
541
542         if (hammer2_ioctl_pfs_lookup(ip, pfs) == 0)
543                 return(EEXIST);
544
545         hammer2_trans_init(&trans, ip->pmp, HAMMER2_TRANS_NEWINODE);
546         nip = hammer2_inode_create(&trans, hmp->spmp->iroot, NULL, NULL,
547                                      pfs->name, strlen(pfs->name),
548                                      &ncluster, &error);
549         if (error == 0) {
550                 nipdata = hammer2_cluster_modify_ip(&trans, nip, ncluster, 0);
551                 nipdata->pfs_type = pfs->pfs_type;
552                 nipdata->pfs_clid = pfs->pfs_clid;
553                 nipdata->pfs_fsid = pfs->pfs_fsid;
554
555                 /*
556                  * Do not allow compression on PFS's with the special name
557                  * "boot", the boot loader can't decompress (yet).
558                  */
559                 if (strcmp(pfs->name, "boot") == 0)
560                         nipdata->comp_algo = HAMMER2_ENC_ALGO(
561                                                         HAMMER2_COMP_AUTOZERO);
562                 hammer2_cluster_modsync(ncluster);
563                 hammer2_inode_unlock_ex(nip, ncluster);
564         }
565         hammer2_trans_done(&trans);
566
567         return (error);
568 }
569
570 /*
571  * Destroy an existing PFS under the super-root
572  */
573 static int
574 hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
575 {
576         hammer2_mount_t *hmp;
577         hammer2_ioc_pfs_t *pfs = data;
578         hammer2_trans_t trans;
579         int error;
580
581         hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
582         hammer2_trans_init(&trans, ip->pmp, 0);
583         error = hammer2_unlink_file(&trans, hmp->spmp->iroot,
584                                     pfs->name, strlen(pfs->name),
585                                     2, NULL, NULL, -1);
586         hammer2_trans_done(&trans);
587
588         return (error);
589 }
590
591 static int
592 hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data)
593 {
594         hammer2_ioc_pfs_t *pfs = data;
595         hammer2_trans_t trans;
596         hammer2_cluster_t *cparent;
597         int error;
598
599         if (pfs->name[0] == 0)
600                 return(EINVAL);
601         if (pfs->name[sizeof(pfs->name)-1] != 0)
602                 return(EINVAL);
603
604         hammer2_vfs_sync(ip->pmp->mp, MNT_WAIT);
605
606         hammer2_trans_init(&trans, ip->pmp,
607                            HAMMER2_TRANS_ISFLUSH | HAMMER2_TRANS_NEWINODE);
608         cparent = hammer2_inode_lock_ex(ip);
609         error = hammer2_cluster_snapshot(&trans, cparent, pfs);
610         hammer2_inode_unlock_ex(ip, cparent);
611         hammer2_trans_done(&trans);
612
613         return (error);
614 }
615
616 /*
617  * Retrieve the raw inode structure
618  */
619 static int
620 hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data)
621 {
622         const hammer2_inode_data_t *ripdata;
623         hammer2_ioc_inode_t *ino;
624         hammer2_cluster_t *cparent;
625
626         ino = data;
627
628         cparent = hammer2_inode_lock_sh(ip);
629         ripdata = &hammer2_cluster_rdata(cparent)->ipdata;
630         ino->ip_data = *ripdata;
631         ino->kdata = ip;
632         hammer2_inode_unlock_sh(ip, cparent);
633
634         return (0);
635 }
636
637 /*
638  * Set various parameters in an inode which cannot be set through
639  * normal filesystem VNOPS.
640  */
641 static int
642 hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
643 {
644         const hammer2_inode_data_t *ripdata;
645         hammer2_inode_data_t *wipdata;
646         hammer2_ioc_inode_t *ino = data;
647         hammer2_cluster_t *cparent;
648         hammer2_trans_t trans;
649         int error = 0;
650         int dosync = 0;
651
652         hammer2_trans_init(&trans, ip->pmp, 0);
653         cparent = hammer2_inode_lock_ex(ip);
654         ripdata = &hammer2_cluster_rdata(cparent)->ipdata;
655
656         if (ino->ip_data.check_algo != ripdata->check_algo) {
657                 wipdata = hammer2_cluster_modify_ip(&trans, ip, cparent, 0);
658                 wipdata->check_algo = ino->ip_data.check_algo;
659                 ripdata = wipdata; /* safety */
660                 hammer2_cluster_setmethod_check(&trans, cparent,
661                                                 wipdata->check_algo);
662                 dosync = 1;
663         }
664         if (ino->ip_data.comp_algo != ripdata->comp_algo) {
665                 wipdata = hammer2_cluster_modify_ip(&trans, ip, cparent, 0);
666                 wipdata->comp_algo = ino->ip_data.comp_algo;
667                 ripdata = wipdata; /* safety */
668                 dosync = 1;
669         }
670         ino->kdata = ip;
671         
672         /* Ignore these flags for now...*/
673         if (ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) {
674         }
675         if (ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) {
676         }
677         if (ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) {
678         }
679         if (dosync)
680                 hammer2_cluster_modsync(cparent);
681         hammer2_trans_done(&trans);
682         hammer2_inode_unlock_ex(ip, cparent);
683
684         return (error);
685 }
686
687 static
688 int
689 hammer2_ioctl_debug_dump(hammer2_inode_t *ip)
690 {
691         hammer2_chain_t *chain;
692         int count = 1000;
693         int i;
694
695         for (i = 0; i < ip->cluster.nchains; ++i) {
696                 chain = ip->cluster.array[i].chain;
697                 if (chain == NULL)
698                         continue;
699                 hammer2_dump_chain(chain, 0, &count, 'i');
700         }
701         return 0;
702 }
703
704 static
705 int
706 hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data)
707 {
708         hammer2_ioc_bulkfree_t *bfi = data;
709         hammer2_mount_t *hmp = ip->pmp->iroot->cluster.focus->hmp;
710         int error;
711
712         /* XXX run local cluster targets only */
713         error = hammer2_bulkfree_pass(hmp, bfi);
714
715         return error;
716 }