xref: /dflybsd-src/sys/vfs/hammer2/hammer2_ioctl.c (revision d63676ccce44debffd22823892f437449f6acdad)
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 a particular PFS by key, specify the key but note that
366  * the ioctl will return the lowest key >= specified_key, so the caller
367  * must verify the key.
368  *
369  * To retrieve the PFS associated with the file descriptor, pass
370  * name_key set to (hammer2_key_t)-1.
371  */
372 static int
373 hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data)
374 {
375 	const hammer2_inode_data_t *ripdata;
376 	hammer2_dev_t *hmp;
377 	hammer2_ioc_pfs_t *pfs;
378 	hammer2_chain_t *parent;
379 	hammer2_chain_t *chain;
380 	hammer2_key_t key_next;
381 	hammer2_key_t save_key;
382 	int cache_index = -1;
383 	int error;
384 
385 	error = 0;
386 	hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
387 	pfs = data;
388 
389 	save_key = pfs->name_key;
390 
391 	/*
392 	 * Setup
393 	 */
394 	if (save_key == (hammer2_key_t)-1) {
395 		hammer2_inode_lock(ip->pmp->iroot, 0);
396 		parent = NULL;
397 		chain = hammer2_inode_chain(hmp->spmp->iroot, 0,
398 					    HAMMER2_RESOLVE_ALWAYS |
399 					    HAMMER2_RESOLVE_SHARED);
400 	} else {
401 		hammer2_inode_lock(hmp->spmp->iroot, 0);
402 		parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
403 					    HAMMER2_RESOLVE_ALWAYS |
404 					    HAMMER2_RESOLVE_SHARED);
405 		chain = hammer2_chain_lookup(&parent, &key_next,
406 					    pfs->name_key, HAMMER2_KEY_MAX,
407 					    &cache_index,
408 					    HAMMER2_LOOKUP_SHARED);
409 	}
410 
411 	/*
412 	 * Locate next PFS
413 	 */
414 	while (chain) {
415 		if (chain->bref.type == HAMMER2_BREF_TYPE_INODE)
416 			break;
417 		if (parent == NULL) {
418 			hammer2_chain_unlock(chain);
419 			hammer2_chain_drop(chain);
420 			break;
421 		}
422 		chain = hammer2_chain_next(&parent, chain, &key_next,
423 					    key_next, HAMMER2_KEY_MAX,
424 					    &cache_index,
425 					    HAMMER2_LOOKUP_SHARED);
426 	}
427 
428 	/*
429 	 * Load the data being returned by the ioctl.
430 	 */
431 	if (chain) {
432 		ripdata = &chain->data->ipdata;
433 		pfs->name_key = ripdata->meta.name_key;
434 		pfs->pfs_type = ripdata->meta.pfs_type;
435 		pfs->pfs_subtype = ripdata->meta.pfs_subtype;
436 		pfs->pfs_clid = ripdata->meta.pfs_clid;
437 		pfs->pfs_fsid = ripdata->meta.pfs_fsid;
438 		KKASSERT(ripdata->meta.name_len < sizeof(pfs->name));
439 		bcopy(ripdata->filename, pfs->name, ripdata->meta.name_len);
440 		pfs->name[ripdata->meta.name_len] = 0;
441 		ripdata = NULL;	/* safety */
442 
443 		/*
444 		 * Calculate name_next.
445 		 */
446 		chain = hammer2_chain_next(&parent, chain, &key_next,
447 					    key_next, HAMMER2_KEY_MAX,
448 					    &cache_index,
449 					    HAMMER2_LOOKUP_SHARED);
450 		if (chain)
451 			pfs->name_next = chain->bref.key;
452 		else
453 			pfs->name_next = (hammer2_key_t)-1;
454 	} else {
455 		pfs->name_next = (hammer2_key_t)-1;
456 		error = ENOENT;
457 	}
458 
459 	/*
460 	 * Cleanup
461 	 */
462 	if (chain) {
463 		hammer2_chain_unlock(chain);
464 		hammer2_chain_drop(chain);
465 	}
466 	if (parent) {
467 		hammer2_chain_unlock(parent);
468 		hammer2_chain_drop(parent);
469 	}
470 	if (save_key == (hammer2_key_t)-1) {
471 		hammer2_inode_unlock(ip->pmp->iroot);
472 	} else {
473 		hammer2_inode_unlock(hmp->spmp->iroot);
474 	}
475 
476 	return (error);
477 }
478 
479 /*
480  * Find a specific PFS by name
481  */
482 static int
483 hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data)
484 {
485 	const hammer2_inode_data_t *ripdata;
486 	hammer2_dev_t *hmp;
487 	hammer2_ioc_pfs_t *pfs;
488 	hammer2_chain_t *parent;
489 	hammer2_chain_t *chain;
490 	hammer2_key_t key_next;
491 	hammer2_key_t lhc;
492 	int cache_index = -1;
493 	int error;
494 	size_t len;
495 
496 	error = 0;
497 	hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
498 	pfs = data;
499 	hammer2_inode_lock(hmp->spmp->iroot, HAMMER2_RESOLVE_SHARED);
500 	parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
501 				     HAMMER2_RESOLVE_ALWAYS |
502 				     HAMMER2_RESOLVE_SHARED);
503 
504 	pfs->name[sizeof(pfs->name) - 1] = 0;
505 	len = strlen(pfs->name);
506 	lhc = hammer2_dirhash(pfs->name, len);
507 
508 	chain = hammer2_chain_lookup(&parent, &key_next,
509 					 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
510 					 &cache_index,
511 					 HAMMER2_LOOKUP_SHARED);
512 	while (chain) {
513 		if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
514 			ripdata = &chain->data->ipdata;
515 			if (ripdata->meta.name_len == len &&
516 			    bcmp(ripdata->filename, pfs->name, len) == 0) {
517 				break;
518 			}
519 			ripdata = NULL;	/* safety */
520 		}
521 		chain = hammer2_chain_next(&parent, chain, &key_next,
522 					   key_next,
523 					   lhc + HAMMER2_DIRHASH_LOMASK,
524 					   &cache_index,
525 					   HAMMER2_LOOKUP_SHARED);
526 	}
527 
528 	/*
529 	 * Load the data being returned by the ioctl.
530 	 */
531 	if (chain) {
532 		ripdata = &chain->data->ipdata;
533 		pfs->name_key = ripdata->meta.name_key;
534 		pfs->pfs_type = ripdata->meta.pfs_type;
535 		pfs->pfs_subtype = ripdata->meta.pfs_subtype;
536 		pfs->pfs_clid = ripdata->meta.pfs_clid;
537 		pfs->pfs_fsid = ripdata->meta.pfs_fsid;
538 		ripdata = NULL;
539 
540 		hammer2_chain_unlock(chain);
541 		hammer2_chain_drop(chain);
542 	} else {
543 		error = ENOENT;
544 	}
545 	if (parent) {
546 		hammer2_chain_unlock(parent);
547 		hammer2_chain_drop(parent);
548 	}
549 	hammer2_inode_unlock(hmp->spmp->iroot);
550 
551 	return (error);
552 }
553 
554 /*
555  * Create a new PFS under the super-root
556  */
557 static int
558 hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data)
559 {
560 	hammer2_inode_data_t *nipdata;
561 	hammer2_chain_t *nchain;
562 	hammer2_dev_t *hmp;
563 	hammer2_ioc_pfs_t *pfs;
564 	hammer2_inode_t *nip;
565 	int error;
566 
567 	hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */
568 	pfs = data;
569 	nip = NULL;
570 
571 	if (pfs->name[0] == 0)
572 		return(EINVAL);
573 	pfs->name[sizeof(pfs->name) - 1] = 0;	/* ensure 0-termination */
574 
575 	if (hammer2_ioctl_pfs_lookup(ip, pfs) == 0)
576 		return(EEXIST);
577 
578 	hammer2_trans_init(hmp->spmp, 0);
579 	nip = hammer2_inode_create(hmp->spmp->iroot, NULL, NULL,
580 				   pfs->name, strlen(pfs->name), 0,
581 				   1, HAMMER2_OBJTYPE_DIRECTORY, 0,
582 				   HAMMER2_INSERT_PFSROOT, &error);
583 	if (error == 0) {
584 		hammer2_inode_modify(nip);
585 		nchain = hammer2_inode_chain(nip, 0, HAMMER2_RESOLVE_ALWAYS);
586 		hammer2_chain_modify(nchain, 0);
587 		nipdata = &nchain->data->ipdata;
588 
589 		nip->meta.pfs_type = pfs->pfs_type;
590 		nip->meta.pfs_subtype = pfs->pfs_subtype;
591 		nip->meta.pfs_clid = pfs->pfs_clid;
592 		nip->meta.pfs_fsid = pfs->pfs_fsid;
593 		nip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT;
594 
595 		/*
596 		 * Set default compression and check algorithm.  This
597 		 * can be changed later.
598 		 *
599 		 * Do not allow compression on PFS's with the special name
600 		 * "boot", the boot loader can't decompress (yet).
601 		 */
602 		nip->meta.comp_algo =
603 			HAMMER2_ENC_ALGO(HAMMER2_COMP_NEWFS_DEFAULT);
604 		nip->meta.check_algo =
605 			HAMMER2_ENC_ALGO( HAMMER2_CHECK_ISCSI32);
606 
607 		if (strcasecmp(pfs->name, "boot") == 0) {
608 			nip->meta.comp_algo =
609 				HAMMER2_ENC_ALGO(HAMMER2_COMP_AUTOZERO);
610 		}
611 #if 0
612 		hammer2_blockref_t bref;
613 		/* XXX new PFS needs to be rescanned / added */
614 		bref = nchain->bref;
615 		kprintf("ADD LOCAL PFS (IOCTL): %s\n", nipdata->filename);
616 		hammer2_pfsalloc(nchain, nipdata, bref.modify_tid);
617 #endif
618 		/* XXX rescan */
619 		hammer2_chain_unlock(nchain);
620 		hammer2_chain_drop(nchain);
621 
622 		hammer2_inode_unlock(nip);
623 	}
624 	hammer2_trans_done(hmp->spmp);
625 
626 	return (error);
627 }
628 
629 /*
630  * Destroy an existing PFS under the super-root
631  */
632 static int
633 hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
634 {
635 	hammer2_ioc_pfs_t *pfs = data;
636 	hammer2_pfs_t *spmp;
637 	hammer2_xop_unlink_t *xop;
638 	hammer2_inode_t *dip;
639 	int error;
640 
641 	pfs->name[sizeof(pfs->name) - 1] = 0;	/* ensure termination */
642 
643 	/* XXX */
644 	spmp = ip->pmp->iroot->cluster.focus->hmp->spmp;
645 	dip = spmp->iroot;
646 	hammer2_trans_init(spmp, 0);
647 	hammer2_inode_lock(dip, 0);
648 
649 	xop = &hammer2_xop_alloc(dip)->xop_unlink;
650 	hammer2_xop_setname(&xop->head, pfs->name, strlen(pfs->name));
651 	xop->isdir = 2;
652 	xop->dopermanent = 1;
653 	hammer2_xop_start(&xop->head, hammer2_xop_unlink);
654 
655 	error = hammer2_xop_collect(&xop->head, 0);
656 
657 	hammer2_inode_unlock(dip);
658 	hammer2_trans_done(spmp);
659 
660 	return (error);
661 }
662 
663 static int
664 hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data)
665 {
666 	hammer2_ioc_pfs_t *pfs = data;
667 	hammer2_cluster_t *cparent;
668 	int error;
669 
670 	if (pfs->name[0] == 0)
671 		return(EINVAL);
672 	if (pfs->name[sizeof(pfs->name)-1] != 0)
673 		return(EINVAL);
674 
675 	hammer2_vfs_sync(ip->pmp->mp, MNT_WAIT);
676 
677 	hammer2_trans_init(ip->pmp, HAMMER2_TRANS_ISFLUSH);
678 	hammer2_inode_lock(ip, 0);
679 	cparent = hammer2_inode_cluster(ip, HAMMER2_RESOLVE_ALWAYS);
680 	error = hammer2_cluster_snapshot(cparent, pfs);
681 	hammer2_inode_unlock(ip);
682 	hammer2_cluster_unlock(cparent);
683 	hammer2_cluster_drop(cparent);
684 	hammer2_trans_done(ip->pmp);
685 
686 	return (error);
687 }
688 
689 /*
690  * Retrieve the raw inode structure, non-inclusive of node-specific data.
691  */
692 static int
693 hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data)
694 {
695 	hammer2_ioc_inode_t *ino;
696 	int error;
697 
698 	ino = data;
699 
700 	hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
701 	if (ip->cluster.focus) {
702 		/* XXX */
703 		ino->data_count = ip->cluster.focus->bref.data_count;
704 		ino->inode_count = ip->cluster.focus->bref.inode_count;
705 		error = 0;
706 	} else {
707 		ino->data_count = -1;
708 		ino->inode_count = -1;
709 		error = EIO;
710 	}
711 	bzero(&ino->ip_data, sizeof(ino->ip_data));
712 	ino->ip_data.meta = ip->meta;
713 	ino->kdata = ip;
714 	hammer2_inode_unlock(ip);
715 
716 	return error;
717 }
718 
719 /*
720  * Set various parameters in an inode which cannot be set through
721  * normal filesystem VNOPS.
722  */
723 static int
724 hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
725 {
726 	hammer2_ioc_inode_t *ino = data;
727 	int error = 0;
728 
729 	hammer2_trans_init(ip->pmp, 0);
730 	hammer2_inode_lock(ip, 0);
731 
732 	if ((ino->flags & HAMMER2IOC_INODE_FLAG_CHECK) &&
733 	    ip->meta.check_algo != ino->ip_data.meta.check_algo) {
734 		hammer2_inode_modify(ip);
735 		ip->meta.check_algo = ino->ip_data.meta.check_algo;
736 	}
737 	if ((ino->flags & HAMMER2IOC_INODE_FLAG_COMP) &&
738 	    ip->meta.comp_algo != ino->ip_data.meta.comp_algo) {
739 		hammer2_inode_modify(ip);
740 		ip->meta.comp_algo = ino->ip_data.meta.comp_algo;
741 	}
742 	ino->kdata = ip;
743 
744 	/* Ignore these flags for now...*/
745 	if ((ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) &&
746 	    ip->meta.inode_quota != ino->ip_data.meta.inode_quota) {
747 		hammer2_inode_modify(ip);
748 		ip->meta.inode_quota = ino->ip_data.meta.inode_quota;
749 	}
750 	if ((ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) &&
751 	    ip->meta.data_quota != ino->ip_data.meta.data_quota) {
752 		hammer2_inode_modify(ip);
753 		ip->meta.data_quota = ino->ip_data.meta.data_quota;
754 	}
755 	if ((ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) &&
756 	    ip->meta.ncopies != ino->ip_data.meta.ncopies) {
757 		hammer2_inode_modify(ip);
758 		ip->meta.ncopies = ino->ip_data.meta.ncopies;
759 	}
760 	hammer2_inode_unlock(ip);
761 	hammer2_trans_done(ip->pmp);
762 
763 	return (error);
764 }
765 
766 static
767 int
768 hammer2_ioctl_debug_dump(hammer2_inode_t *ip)
769 {
770 	hammer2_chain_t *chain;
771 	int count = 1000;
772 	int i;
773 
774 	for (i = 0; i < ip->cluster.nchains; ++i) {
775 		chain = ip->cluster.array[i].chain;
776 		if (chain == NULL)
777 			continue;
778 		hammer2_dump_chain(chain, 0, &count, 'i');
779 	}
780 	return 0;
781 }
782 
783 static
784 int
785 hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data)
786 {
787 	hammer2_ioc_bulkfree_t *bfi = data;
788 	hammer2_dev_t *hmp = ip->pmp->iroot->cluster.focus->hmp;
789 	int error;
790 
791 	/* XXX run local cluster targets only */
792 	error = hammer2_bulkfree_pass(hmp, bfi);
793 
794 	return error;
795 }
796