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