usr.sbin/makefs: Sync with sys/vfs/hammer2
[dragonfly.git] / usr.sbin / makefs / hammer2 / hammer2_ioctl.c
blob126ecdf15e07d98b62f6717e7e5b78d0320968c4
1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 2023 Tomohiro Kusumi <tkusumi@netbsd.org>
5 * Copyright (c) 2011-2023 The DragonFly Project. All rights reserved.
7 * This code is derived from software contributed to The DragonFly Project
8 * by Matthew Dillon <dillon@dragonflybsd.org>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 * 3. Neither the name of The DragonFly Project nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific, prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
38 * Ioctl Functions.
40 * WARNING! The ioctl functions which manipulate the connection state need
41 * to be able to run without deadlock on the volume's chain lock.
42 * Most of these functions use a separate lock.
45 #include "hammer2.h"
47 #if 0
48 #include <sys/kern_syscall.h>
50 static int hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data);
51 static int hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data);
52 static int hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data);
53 static int hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data);
54 static int hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data);
55 static int hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data);
56 static int hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data);
57 static int hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data);
58 static int hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data);
59 static int hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data);
60 static int hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data);
61 static int hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data);
62 static int hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data);
63 static int hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data);
64 static int hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data);
65 static int hammer2_ioctl_debug_dump(hammer2_inode_t *ip, u_int flags);
66 static int hammer2_ioctl_emerg_mode(hammer2_inode_t *ip, u_int mode);
67 static int hammer2_ioctl_growfs(hammer2_inode_t *ip, void *data,
68 struct ucred *cred);
69 //static int hammer2_ioctl_inode_comp_set(hammer2_inode_t *ip, void *data);
70 //static int hammer2_ioctl_inode_comp_rec_set(hammer2_inode_t *ip, void *data);
71 //static int hammer2_ioctl_inode_comp_rec_set2(hammer2_inode_t *ip, void *data);
72 static int hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data);
73 static int hammer2_ioctl_destroy(hammer2_inode_t *ip, void *data);
74 static int hammer2_ioctl_volume_list(hammer2_inode_t *ip, void *data);
76 int
77 hammer2_ioctl(hammer2_inode_t *ip, u_long com, void *data, int fflag,
78 struct ucred *cred)
80 int error;
83 * Standard root cred checks, will be selectively ignored below
84 * for ioctls that do not require root creds.
86 error = priv_check_cred(cred, PRIV_HAMMER_IOCTL, 0);
88 switch(com) {
89 case HAMMER2IOC_VERSION_GET:
90 error = hammer2_ioctl_version_get(ip, data);
91 break;
92 case HAMMER2IOC_RECLUSTER:
93 if (error == 0)
94 error = hammer2_ioctl_recluster(ip, data);
95 break;
96 case HAMMER2IOC_REMOTE_SCAN:
97 if (error == 0)
98 error = hammer2_ioctl_remote_scan(ip, data);
99 break;
100 case HAMMER2IOC_REMOTE_ADD:
101 if (error == 0)
102 error = hammer2_ioctl_remote_add(ip, data);
103 break;
104 case HAMMER2IOC_REMOTE_DEL:
105 if (error == 0)
106 error = hammer2_ioctl_remote_del(ip, data);
107 break;
108 case HAMMER2IOC_REMOTE_REP:
109 if (error == 0)
110 error = hammer2_ioctl_remote_rep(ip, data);
111 break;
112 case HAMMER2IOC_SOCKET_GET:
113 if (error == 0)
114 error = hammer2_ioctl_socket_get(ip, data);
115 break;
116 case HAMMER2IOC_SOCKET_SET:
117 if (error == 0)
118 error = hammer2_ioctl_socket_set(ip, data);
119 break;
120 case HAMMER2IOC_PFS_GET:
121 if (error == 0)
122 error = hammer2_ioctl_pfs_get(ip, data);
123 break;
124 case HAMMER2IOC_PFS_LOOKUP:
125 if (error == 0)
126 error = hammer2_ioctl_pfs_lookup(ip, data);
127 break;
128 case HAMMER2IOC_PFS_CREATE:
129 if (error == 0)
130 error = hammer2_ioctl_pfs_create(ip, data);
131 break;
132 case HAMMER2IOC_PFS_DELETE:
133 if (error == 0)
134 error = hammer2_ioctl_pfs_delete(ip, data);
135 break;
136 case HAMMER2IOC_PFS_SNAPSHOT:
137 if (error == 0)
138 error = hammer2_ioctl_pfs_snapshot(ip, data);
139 break;
140 case HAMMER2IOC_INODE_GET:
141 error = hammer2_ioctl_inode_get(ip, data);
142 break;
143 case HAMMER2IOC_INODE_SET:
144 if (error == 0)
145 error = hammer2_ioctl_inode_set(ip, data);
146 break;
147 case HAMMER2IOC_BULKFREE_SCAN:
148 error = hammer2_ioctl_bulkfree_scan(ip, data);
149 break;
150 case HAMMER2IOC_BULKFREE_ASYNC:
151 error = hammer2_ioctl_bulkfree_scan(ip, NULL);
152 break;
153 case HAMMER2IOC_DESTROY:
154 if (error == 0)
155 error = hammer2_ioctl_destroy(ip, data);
156 break;
157 case HAMMER2IOC_DEBUG_DUMP:
158 error = hammer2_ioctl_debug_dump(ip, *(u_int *)data);
159 break;
160 case HAMMER2IOC_EMERG_MODE:
161 if (error == 0)
162 error = hammer2_ioctl_emerg_mode(ip, *(u_int *)data);
163 break;
164 case HAMMER2IOC_GROWFS:
165 if (error == 0)
166 error = hammer2_ioctl_growfs(ip, data, cred);
167 break;
168 case HAMMER2IOC_VOLUME_LIST:
169 if (error == 0)
170 error = hammer2_ioctl_volume_list(ip, data);
171 break;
172 default:
173 error = EOPNOTSUPP;
174 break;
176 return (error);
180 * Retrieve version and basic info
182 static int
183 hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data)
185 hammer2_ioc_version_t *version = data;
186 hammer2_dev_t *hmp;
188 hmp = ip->pmp->pfs_hmps[0];
189 if (hmp)
190 version->version = hmp->voldata.version;
191 else
192 version->version = -1;
193 return 0;
196 static int
197 hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data)
199 hammer2_ioc_recluster_t *recl = data;
200 struct vnode *vproot;
201 struct file *fp;
202 hammer2_cluster_t *cluster;
203 int error;
205 fp = holdfp(curthread, recl->fd, -1);
206 if (fp) {
207 error = VFS_ROOT(ip->pmp->mp, &vproot);
208 if (error == 0) {
209 cluster = &ip->pmp->iroot->cluster;
210 kprintf("reconnect to cluster: nc=%d focus=%p\n",
211 cluster->nchains, cluster->focus);
212 if (cluster->nchains != 1 || cluster->focus == NULL) {
213 kprintf("not a local device mount\n");
214 error = EINVAL;
215 } else {
216 hammer2_cluster_reconnect(cluster->focus->hmp,
217 fp);
218 kprintf("ok\n");
219 error = 0;
221 vput(vproot);
223 } else {
224 error = EINVAL;
226 return error;
230 * Retrieve information about a remote
232 static int
233 hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data)
235 hammer2_dev_t *hmp;
236 hammer2_ioc_remote_t *remote = data;
237 int copyid = remote->copyid;
239 hmp = ip->pmp->pfs_hmps[0];
240 if (hmp == NULL)
241 return (EINVAL);
243 if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
244 return (EINVAL);
246 hammer2_voldata_lock(hmp);
247 remote->copy1 = hmp->voldata.copyinfo[copyid];
248 hammer2_voldata_unlock(hmp);
251 * Adjust nextid (GET only)
253 while (++copyid < HAMMER2_COPYID_COUNT &&
254 hmp->voldata.copyinfo[copyid].copyid == 0) {
257 if (copyid == HAMMER2_COPYID_COUNT)
258 remote->nextid = -1;
259 else
260 remote->nextid = copyid;
262 return(0);
266 * Add new remote entry
268 static int
269 hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data)
271 hammer2_ioc_remote_t *remote = data;
272 hammer2_pfs_t *pmp = ip->pmp;
273 hammer2_dev_t *hmp;
274 int copyid = remote->copyid;
275 int error = 0;
277 hmp = pmp->pfs_hmps[0];
278 if (hmp == NULL)
279 return (EINVAL);
280 if (copyid >= HAMMER2_COPYID_COUNT)
281 return (EINVAL);
283 hammer2_voldata_lock(hmp);
284 if (copyid < 0) {
285 for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
286 if (hmp->voldata.copyinfo[copyid].copyid == 0)
287 break;
289 if (copyid == HAMMER2_COPYID_COUNT) {
290 error = ENOSPC;
291 goto failed;
294 hammer2_voldata_modify(hmp);
295 remote->copy1.copyid = copyid;
296 hmp->voldata.copyinfo[copyid] = remote->copy1;
297 hammer2_volconf_update(hmp, copyid);
298 failed:
299 hammer2_voldata_unlock(hmp);
300 return (error);
304 * Delete existing remote entry
306 static int
307 hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data)
309 hammer2_ioc_remote_t *remote = data;
310 hammer2_pfs_t *pmp = ip->pmp;
311 hammer2_dev_t *hmp;
312 int copyid = remote->copyid;
313 int error = 0;
315 hmp = pmp->pfs_hmps[0];
316 if (hmp == NULL)
317 return (EINVAL);
318 if (copyid >= HAMMER2_COPYID_COUNT)
319 return (EINVAL);
320 remote->copy1.path[sizeof(remote->copy1.path) - 1] = 0;
321 hammer2_voldata_lock(hmp);
322 if (copyid < 0) {
323 for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
324 if (hmp->voldata.copyinfo[copyid].copyid == 0)
325 continue;
326 if (strcmp(remote->copy1.path,
327 hmp->voldata.copyinfo[copyid].path) == 0) {
328 break;
331 if (copyid == HAMMER2_COPYID_COUNT) {
332 error = ENOENT;
333 goto failed;
336 hammer2_voldata_modify(hmp);
337 hmp->voldata.copyinfo[copyid].copyid = 0;
338 hammer2_volconf_update(hmp, copyid);
339 failed:
340 hammer2_voldata_unlock(hmp);
341 return (error);
345 * Replace existing remote entry
347 static int
348 hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data)
350 hammer2_ioc_remote_t *remote = data;
351 hammer2_dev_t *hmp;
352 int copyid = remote->copyid;
354 hmp = ip->pmp->pfs_hmps[0];
355 if (hmp == NULL)
356 return (EINVAL);
357 if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
358 return (EINVAL);
360 hammer2_voldata_lock(hmp);
361 hammer2_voldata_modify(hmp);
362 /*hammer2_volconf_update(hmp, copyid);*/
363 hammer2_voldata_unlock(hmp);
365 return(0);
369 * Retrieve communications socket
371 static int
372 hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data)
374 return (EOPNOTSUPP);
378 * Set communications socket for connection
380 static int
381 hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data)
383 hammer2_ioc_remote_t *remote = data;
384 hammer2_dev_t *hmp;
385 int copyid = remote->copyid;
387 hmp = ip->pmp->pfs_hmps[0];
388 if (hmp == NULL)
389 return (EINVAL);
390 if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
391 return (EINVAL);
393 hammer2_voldata_lock(hmp);
394 hammer2_voldata_unlock(hmp);
396 return(0);
400 * Used to scan and retrieve PFS information. PFS's are directories under
401 * the super-root.
403 * To scan PFSs pass name_key=0. The function will scan for the next
404 * PFS and set all fields, as well as set name_next to the next key.
405 * When no PFSs remain, name_next is set to (hammer2_key_t)-1.
407 * To retrieve a particular PFS by key, specify the key but note that
408 * the ioctl will return the lowest key >= specified_key, so the caller
409 * must verify the key.
411 * To retrieve the PFS associated with the file descriptor, pass
412 * name_key set to (hammer2_key_t)-1.
414 static int
415 hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data)
417 const hammer2_inode_data_t *ripdata;
418 hammer2_dev_t *hmp;
419 hammer2_ioc_pfs_t *pfs;
420 hammer2_chain_t *parent;
421 hammer2_chain_t *chain;
422 hammer2_key_t key_next;
423 hammer2_key_t save_key;
424 int error;
426 hmp = ip->pmp->pfs_hmps[0];
427 if (hmp == NULL)
428 return (EINVAL);
430 pfs = data;
431 save_key = pfs->name_key;
432 error = 0;
435 * Setup
437 if (save_key == (hammer2_key_t)-1) {
438 hammer2_inode_lock(ip->pmp->iroot, 0);
439 parent = NULL;
440 chain = hammer2_inode_chain(ip->pmp->iroot, 0,
441 HAMMER2_RESOLVE_ALWAYS |
442 HAMMER2_RESOLVE_SHARED);
443 } else {
444 hammer2_inode_lock(hmp->spmp->iroot, 0);
445 parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
446 HAMMER2_RESOLVE_ALWAYS |
447 HAMMER2_RESOLVE_SHARED);
448 chain = hammer2_chain_lookup(&parent, &key_next,
449 pfs->name_key, HAMMER2_KEY_MAX,
450 &error,
451 HAMMER2_LOOKUP_SHARED);
455 * Locate next PFS
457 while (chain) {
458 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE)
459 break;
460 if (parent == NULL) {
461 hammer2_chain_unlock(chain);
462 hammer2_chain_drop(chain);
463 chain = NULL;
464 break;
466 chain = hammer2_chain_next(&parent, chain, &key_next,
467 key_next, HAMMER2_KEY_MAX,
468 &error,
469 HAMMER2_LOOKUP_SHARED);
471 error = hammer2_error_to_errno(error);
474 * Load the data being returned by the ioctl.
476 if (chain && chain->error == 0) {
477 ripdata = &chain->data->ipdata;
478 pfs->name_key = ripdata->meta.name_key;
479 pfs->pfs_type = ripdata->meta.pfs_type;
480 pfs->pfs_subtype = ripdata->meta.pfs_subtype;
481 pfs->pfs_clid = ripdata->meta.pfs_clid;
482 pfs->pfs_fsid = ripdata->meta.pfs_fsid;
483 KKASSERT(ripdata->meta.name_len < sizeof(pfs->name));
484 bcopy(ripdata->filename, pfs->name, ripdata->meta.name_len);
485 pfs->name[ripdata->meta.name_len] = 0;
486 ripdata = NULL; /* safety */
489 * Calculate name_next, if any. We are only accessing
490 * chain->bref so we can ignore chain->error (if the key
491 * is used later it will error then).
493 if (parent == NULL) {
494 pfs->name_next = (hammer2_key_t)-1;
495 } else {
496 chain = hammer2_chain_next(&parent, chain, &key_next,
497 key_next, HAMMER2_KEY_MAX,
498 &error,
499 HAMMER2_LOOKUP_SHARED);
500 if (chain)
501 pfs->name_next = chain->bref.key;
502 else
503 pfs->name_next = (hammer2_key_t)-1;
505 } else {
506 pfs->name_next = (hammer2_key_t)-1;
507 error = ENOENT;
511 * Cleanup
513 if (chain) {
514 hammer2_chain_unlock(chain);
515 hammer2_chain_drop(chain);
517 if (parent) {
518 hammer2_chain_unlock(parent);
519 hammer2_chain_drop(parent);
521 if (save_key == (hammer2_key_t)-1) {
522 hammer2_inode_unlock(ip->pmp->iroot);
523 } else {
524 hammer2_inode_unlock(hmp->spmp->iroot);
527 return (error);
531 * Find a specific PFS by name
533 static int
534 hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data)
536 const hammer2_inode_data_t *ripdata;
537 hammer2_dev_t *hmp;
538 hammer2_ioc_pfs_t *pfs;
539 hammer2_chain_t *parent;
540 hammer2_chain_t *chain;
541 hammer2_key_t key_next;
542 hammer2_key_t lhc;
543 int error;
544 size_t len;
546 hmp = ip->pmp->pfs_hmps[0];
547 if (hmp == NULL)
548 return (EINVAL);
550 pfs = data;
551 error = 0;
553 hammer2_inode_lock(hmp->spmp->iroot, HAMMER2_RESOLVE_SHARED);
554 parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
555 HAMMER2_RESOLVE_ALWAYS |
556 HAMMER2_RESOLVE_SHARED);
558 pfs->name[sizeof(pfs->name) - 1] = 0;
559 len = strlen(pfs->name);
560 lhc = hammer2_dirhash(pfs->name, len);
562 chain = hammer2_chain_lookup(&parent, &key_next,
563 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
564 &error, HAMMER2_LOOKUP_SHARED);
565 while (chain) {
566 if (hammer2_chain_dirent_test(chain, pfs->name, len))
567 break;
568 chain = hammer2_chain_next(&parent, chain, &key_next,
569 key_next,
570 lhc + HAMMER2_DIRHASH_LOMASK,
571 &error, HAMMER2_LOOKUP_SHARED);
573 error = hammer2_error_to_errno(error);
576 * Load the data being returned by the ioctl.
578 if (chain && chain->error == 0) {
579 KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
580 ripdata = &chain->data->ipdata;
581 pfs->name_key = ripdata->meta.name_key;
582 pfs->pfs_type = ripdata->meta.pfs_type;
583 pfs->pfs_subtype = ripdata->meta.pfs_subtype;
584 pfs->pfs_clid = ripdata->meta.pfs_clid;
585 pfs->pfs_fsid = ripdata->meta.pfs_fsid;
586 ripdata = NULL;
588 hammer2_chain_unlock(chain);
589 hammer2_chain_drop(chain);
590 } else if (error == 0) {
591 error = ENOENT;
593 if (parent) {
594 hammer2_chain_unlock(parent);
595 hammer2_chain_drop(parent);
597 hammer2_inode_unlock(hmp->spmp->iroot);
599 return (error);
603 * Create a new PFS under the super-root
605 static int
606 hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data)
608 hammer2_inode_data_t *nipdata;
609 hammer2_chain_t *nchain;
610 hammer2_dev_t *hmp;
611 hammer2_dev_t *force_local;
612 hammer2_ioc_pfs_t *pfs;
613 hammer2_inode_t *nip;
614 hammer2_tid_t mtid;
615 int error;
617 hmp = ip->pmp->pfs_hmps[0]; /* XXX */
618 if (hmp == NULL)
619 return (EINVAL);
621 pfs = data;
622 nip = NULL;
624 if (pfs->name[0] == 0)
625 return(EINVAL);
626 pfs->name[sizeof(pfs->name) - 1] = 0; /* ensure 0-termination */
628 if (hammer2_ioctl_pfs_lookup(ip, pfs) == 0)
629 return(EEXIST);
631 hammer2_trans_init(hmp->spmp, HAMMER2_TRANS_ISFLUSH);
632 mtid = hammer2_trans_sub(hmp->spmp);
633 nip = hammer2_inode_create_pfs(hmp->spmp, pfs->name, strlen(pfs->name),
634 &error);
635 if (error == 0) {
636 atomic_set_int(&nip->flags, HAMMER2_INODE_NOSIDEQ);
637 hammer2_inode_modify(nip);
638 nchain = hammer2_inode_chain(nip, 0, HAMMER2_RESOLVE_ALWAYS);
639 error = hammer2_chain_modify(nchain, mtid, 0, 0);
640 KKASSERT(error == 0);
641 nipdata = &nchain->data->ipdata;
643 nip->meta.pfs_type = pfs->pfs_type;
644 nip->meta.pfs_subtype = pfs->pfs_subtype;
645 nip->meta.pfs_clid = pfs->pfs_clid;
646 nip->meta.pfs_fsid = pfs->pfs_fsid;
647 nip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT;
650 * Set default compression and check algorithm. This
651 * can be changed later.
653 * Do not allow compression on PFS's with the special name
654 * "boot", the boot loader can't decompress (yet).
656 nip->meta.comp_algo =
657 HAMMER2_ENC_ALGO(HAMMER2_COMP_NEWFS_DEFAULT);
658 nip->meta.check_algo =
659 HAMMER2_ENC_ALGO( HAMMER2_CHECK_XXHASH64);
661 if (strcasecmp(pfs->name, "boot") == 0) {
662 nip->meta.comp_algo =
663 HAMMER2_ENC_ALGO(HAMMER2_COMP_AUTOZERO);
667 * Super-root isn't mounted, fsync it
669 hammer2_chain_unlock(nchain);
670 hammer2_inode_ref(nip);
671 hammer2_inode_unlock(nip);
672 hammer2_inode_chain_sync(nip);
673 hammer2_inode_chain_flush(nip, HAMMER2_XOP_INODE_STOP |
674 HAMMER2_XOP_FSSYNC);
675 hammer2_inode_drop(nip);
676 /* nip is dead */
679 * We still have a ref on the chain, relock and associate
680 * with an appropriate PFS.
682 force_local = (hmp->hflags & HMNT2_LOCAL) ? hmp : NULL;
684 hammer2_chain_lock(nchain, HAMMER2_RESOLVE_ALWAYS);
685 nipdata = &nchain->data->ipdata;
686 kprintf("ADD LOCAL PFS (IOCTL): %s\n", nipdata->filename);
687 hammer2_pfsalloc(nchain, nipdata, force_local);
689 hammer2_chain_unlock(nchain);
690 hammer2_chain_drop(nchain);
692 hammer2_trans_done(hmp->spmp, HAMMER2_TRANS_ISFLUSH |
693 HAMMER2_TRANS_SIDEQ);
695 return (error);
699 * Destroy an existing PFS under the super-root
701 static int
702 hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
704 hammer2_ioc_pfs_t *pfs = data;
705 hammer2_dev_t *hmp;
706 hammer2_pfs_t *spmp;
707 hammer2_pfs_t *pmp;
708 hammer2_xop_unlink_t *xop;
709 hammer2_inode_t *dip;
710 hammer2_inode_t *iroot;
711 int error;
712 int i;
715 * The PFS should be probed, so we should be able to
716 * locate it. We only delete the PFS from the
717 * specific H2 block device (hmp), not all of
718 * them. We must remove the PFS from the cluster
719 * before we can destroy it.
721 hmp = ip->pmp->pfs_hmps[0];
722 if (hmp == NULL)
723 return (EINVAL);
725 pfs->name[sizeof(pfs->name) - 1] = 0; /* ensure termination */
727 lockmgr(&hammer2_mntlk, LK_EXCLUSIVE);
729 TAILQ_FOREACH(pmp, &hammer2_pfslist, mntentry) {
730 for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
731 if (pmp->pfs_hmps[i] != hmp)
732 continue;
733 if (pmp->pfs_names[i] &&
734 strcmp(pmp->pfs_names[i], pfs->name) == 0) {
735 break;
738 if (i != HAMMER2_MAXCLUSTER)
739 break;
742 if (pmp == NULL) {
743 lockmgr(&hammer2_mntlk, LK_RELEASE);
744 return ENOENT;
746 if (pmp->mp) {
747 lockmgr(&hammer2_mntlk, LK_RELEASE);
748 return EBUSY;
752 * Ok, we found the pmp and we have the index. Permanently remove
753 * the PFS from the cluster
755 iroot = pmp->iroot;
756 kprintf("FOUND PFS %s CLINDEX %d\n", pfs->name, i);
757 hammer2_pfsdealloc(pmp, i, 1);
759 lockmgr(&hammer2_mntlk, LK_RELEASE);
762 * Now destroy the PFS under its device using the per-device
763 * super-root.
765 spmp = hmp->spmp;
766 dip = spmp->iroot;
767 hammer2_trans_init(spmp, 0);
768 hammer2_inode_lock(dip, 0);
770 xop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING);
771 hammer2_xop_setname(&xop->head, pfs->name, strlen(pfs->name));
772 xop->isdir = 2;
773 xop->dopermanent = H2DOPERM_PERMANENT | H2DOPERM_FORCE;
774 hammer2_xop_start(&xop->head, &hammer2_unlink_desc);
776 error = hammer2_xop_collect(&xop->head, 0);
778 hammer2_inode_unlock(dip);
780 #if 0
781 if (error == 0) {
782 ip = hammer2_inode_get(dip->pmp, &xop->head, -1, -1);
783 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
784 if (ip) {
785 hammer2_inode_unlink_finisher(ip, NULL);
786 hammer2_inode_unlock(ip);
788 } else {
789 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
791 #endif
792 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
794 hammer2_trans_done(spmp, HAMMER2_TRANS_SIDEQ);
796 return (hammer2_error_to_errno(error));
799 static int
800 hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data)
802 hammer2_ioc_pfs_t *pfs = data;
803 hammer2_dev_t *hmp;
804 hammer2_pfs_t *pmp;
805 hammer2_chain_t *chain;
806 hammer2_inode_t *nip;
807 hammer2_tid_t mtid;
808 size_t name_len;
809 hammer2_key_t lhc;
810 int error;
811 #if 0
812 uuid_t opfs_clid;
813 #endif
815 if (pfs->name[0] == 0)
816 return(EINVAL);
817 if (pfs->name[sizeof(pfs->name)-1] != 0)
818 return(EINVAL);
820 pmp = ip->pmp;
821 ip = pmp->iroot;
823 hmp = pmp->pfs_hmps[0];
824 if (hmp == NULL)
825 return (EINVAL);
827 lockmgr(&hmp->bulklk, LK_EXCLUSIVE);
830 * NOSYNC is for debugging. We skip the filesystem sync and use
831 * a normal transaction (which is less likely to stall). used for
832 * testing filesystem consistency.
834 * In normal mode we sync the filesystem and use a flush transaction.
836 if (pfs->pfs_flags & HAMMER2_PFSFLAGS_NOSYNC) {
837 hammer2_trans_init(pmp, 0);
838 } else {
839 hammer2_vfs_sync(pmp->mp, MNT_WAIT);
840 hammer2_trans_init(pmp, HAMMER2_TRANS_ISFLUSH);
842 mtid = hammer2_trans_sub(pmp);
843 hammer2_inode_lock(ip, 0);
844 hammer2_inode_modify(ip);
845 ip->meta.pfs_lsnap_tid = mtid;
847 /* XXX cluster it! */
848 chain = hammer2_inode_chain(ip, 0, HAMMER2_RESOLVE_ALWAYS);
850 name_len = strlen(pfs->name);
851 lhc = hammer2_dirhash(pfs->name, name_len);
854 * Get the clid
856 hmp = chain->hmp;
859 * Create the snapshot directory under the super-root
861 * Set PFS type, generate a unique filesystem id, and generate
862 * a cluster id. Use the same clid when snapshotting a PFS root,
863 * which theoretically allows the snapshot to be used as part of
864 * the same cluster (perhaps as a cache).
866 * Note that pfs_lsnap_tid must be set in the snapshot as well,
867 * ensuring that any nocrc/nocomp file data modifications force
868 * a copy-on-write.
870 * Copy the (flushed) blockref array. Theoretically we could use
871 * chain_duplicate() but it becomes difficult to disentangle
872 * the shared core so for now just brute-force it.
874 hammer2_chain_unlock(chain);
875 nip = hammer2_inode_create_pfs(hmp->spmp, pfs->name, name_len, &error);
876 hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS);
878 if (nip) {
879 hammer2_dev_t *force_local;
880 hammer2_chain_t *nchain;
881 hammer2_inode_data_t *wipdata;
882 hammer2_tid_t starting_inum;
884 atomic_set_int(&nip->flags, HAMMER2_INODE_NOSIDEQ);
885 hammer2_inode_modify(nip);
886 nchain = hammer2_inode_chain(nip, 0, HAMMER2_RESOLVE_ALWAYS);
887 error = hammer2_chain_modify(nchain, mtid, 0, 0);
888 KKASSERT(error == 0);
889 wipdata = &nchain->data->ipdata;
891 starting_inum = ip->pmp->inode_tid + 1;
892 nip->meta.pfs_inum = starting_inum;
893 nip->meta.pfs_type = HAMMER2_PFSTYPE_MASTER;
894 nip->meta.pfs_subtype = HAMMER2_PFSSUBTYPE_SNAPSHOT;
895 nip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT;
896 nip->meta.pfs_lsnap_tid = mtid;
897 nchain->bref.embed.stats = chain->bref.embed.stats;
899 kern_uuidgen(&nip->meta.pfs_fsid, 1);
901 #if 0
903 * Give the snapshot its own private cluster id. As a
904 * snapshot no further synchronization with the original
905 * cluster will be done.
907 if (chain->flags & HAMMER2_CHAIN_PFSBOUNDARY)
908 nip->meta.pfs_clid = opfs_clid;
909 else
910 kern_uuidgen(&nip->meta.pfs_clid, 1);
911 #endif
912 kern_uuidgen(&nip->meta.pfs_clid, 1);
913 nchain->bref.flags |= HAMMER2_BREF_FLAG_PFSROOT;
915 /* XXX hack blockset copy */
916 /* XXX doesn't work with real cluster */
917 wipdata->meta = nip->meta;
918 hammer2_spin_ex(&pmp->inum_spin);
919 wipdata->u.blockset = pmp->pfs_iroot_blocksets[0];
920 hammer2_spin_unex(&pmp->inum_spin);
922 KKASSERT(wipdata == &nchain->data->ipdata);
924 hammer2_chain_unlock(nchain);
925 hammer2_inode_ref(nip);
926 hammer2_inode_unlock(nip);
927 hammer2_inode_chain_sync(nip);
928 hammer2_inode_chain_flush(nip, HAMMER2_XOP_INODE_STOP |
929 HAMMER2_XOP_FSSYNC);
930 /* XXX | HAMMER2_XOP_VOLHDR */
931 hammer2_inode_drop(nip);
932 /* nip is dead */
934 force_local = (hmp->hflags & HMNT2_LOCAL) ? hmp : NULL;
936 hammer2_chain_lock(nchain, HAMMER2_RESOLVE_ALWAYS);
937 wipdata = &nchain->data->ipdata;
938 kprintf("SNAPSHOT LOCAL PFS (IOCTL): %s\n", wipdata->filename);
939 hammer2_pfsalloc(nchain, wipdata, force_local);
940 nchain->pmp->inode_tid = starting_inum;
942 hammer2_chain_unlock(nchain);
943 hammer2_chain_drop(nchain);
946 hammer2_chain_unlock(chain);
947 hammer2_chain_drop(chain);
949 hammer2_inode_unlock(ip);
950 if (pfs->pfs_flags & HAMMER2_PFSFLAGS_NOSYNC) {
951 hammer2_trans_done(pmp, 0);
952 } else {
953 hammer2_trans_done(pmp, HAMMER2_TRANS_ISFLUSH |
954 HAMMER2_TRANS_SIDEQ);
957 lockmgr(&hmp->bulklk, LK_RELEASE);
959 return (hammer2_error_to_errno(error));
963 * Retrieve the raw inode structure, non-inclusive of node-specific data.
965 static int
966 hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data)
968 hammer2_ioc_inode_t *ino = data;
970 hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
971 ino->data_count = hammer2_inode_data_count(ip);
972 ino->inode_count = hammer2_inode_inode_count(ip);
974 bzero(&ino->ip_data, sizeof(ino->ip_data));
975 ino->ip_data.meta = ip->meta;
976 hammer2_inode_unlock(ip);
978 return 0;
982 * Set various parameters in an inode which cannot be set through
983 * normal filesystem VNOPS.
985 static int
986 hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
988 hammer2_ioc_inode_t *ino = data;
990 hammer2_trans_init(ip->pmp, 0);
991 hammer2_inode_lock(ip, 0);
993 if ((ino->flags & HAMMER2IOC_INODE_FLAG_CHECK) &&
994 ip->meta.check_algo != ino->ip_data.meta.check_algo) {
995 hammer2_inode_modify(ip);
996 ip->meta.check_algo = ino->ip_data.meta.check_algo;
998 if ((ino->flags & HAMMER2IOC_INODE_FLAG_COMP) &&
999 ip->meta.comp_algo != ino->ip_data.meta.comp_algo) {
1000 hammer2_inode_modify(ip);
1001 ip->meta.comp_algo = ino->ip_data.meta.comp_algo;
1004 /* Ignore these flags for now...*/
1005 if ((ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) &&
1006 ip->meta.inode_quota != ino->ip_data.meta.inode_quota) {
1007 hammer2_inode_modify(ip);
1008 ip->meta.inode_quota = ino->ip_data.meta.inode_quota;
1010 if ((ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) &&
1011 ip->meta.data_quota != ino->ip_data.meta.data_quota) {
1012 hammer2_inode_modify(ip);
1013 ip->meta.data_quota = ino->ip_data.meta.data_quota;
1015 if ((ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) &&
1016 ip->meta.ncopies != ino->ip_data.meta.ncopies) {
1017 hammer2_inode_modify(ip);
1018 ip->meta.ncopies = ino->ip_data.meta.ncopies;
1020 hammer2_inode_unlock(ip);
1021 hammer2_trans_done(ip->pmp, HAMMER2_TRANS_SIDEQ);
1023 return (0);
1026 static
1028 hammer2_ioctl_debug_dump(hammer2_inode_t *ip, u_int flags)
1030 hammer2_chain_t *chain;
1031 int count = 100000;
1032 int i;
1034 for (i = 0; i < ip->cluster.nchains; ++i) {
1035 chain = ip->cluster.array[i].chain;
1036 if (chain == NULL)
1037 continue;
1038 kprintf("cluster #%d\n", i);
1039 hammer2_dump_chain(chain, 0, 0, &count, 'i', flags);
1041 return 0;
1045 * Turn on or off emergency mode on a filesystem.
1047 static
1049 hammer2_ioctl_emerg_mode(hammer2_inode_t *ip, u_int mode)
1051 hammer2_pfs_t *pmp;
1052 hammer2_dev_t *hmp;
1053 int i;
1055 pmp = ip->pmp;
1056 if (mode) {
1057 kprintf("hammer2: WARNING: Emergency mode enabled\n");
1058 atomic_set_int(&pmp->flags, HAMMER2_PMPF_EMERG);
1059 } else {
1060 kprintf("hammer2: WARNING: Emergency mode disabled\n");
1061 atomic_clear_int(&pmp->flags, HAMMER2_PMPF_EMERG);
1063 for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
1064 hmp = pmp->pfs_hmps[i];
1065 if (hmp == NULL)
1066 continue;
1067 if (mode)
1068 atomic_set_int(&hmp->hflags, HMNT2_EMERG);
1069 else
1070 atomic_clear_int(&hmp->hflags, HMNT2_EMERG);
1072 return 0;
1074 #endif
1077 * Do a bulkfree scan on media related to the PFS. This routine will
1078 * flush all PFSs associated with the media before doing the bulkfree
1079 * scan.
1081 * This version can only run on non-clustered media. A new ioctl or a
1082 * temporary mount of @LOCAL will be needed to run on clustered media.
1085 hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data)
1087 hammer2_ioc_bulkfree_t *bfi = data;
1088 hammer2_dev_t *hmp;
1089 hammer2_pfs_t *pmp;
1090 hammer2_chain_t *vchain;
1091 int error;
1092 int didsnap;
1094 pmp = ip->pmp;
1095 ip = pmp->iroot;
1097 hmp = pmp->pfs_hmps[0];
1098 if (hmp == NULL)
1099 return (EINVAL);
1100 if (bfi == NULL)
1101 return (EINVAL);
1104 * Bulkfree has to be serialized to guarantee at least one sync
1105 * inbetween bulkfrees.
1107 error = lockmgr(&hmp->bflock, LK_EXCLUSIVE /*| LK_PCATCH*/);
1108 if (error)
1109 return error;
1112 * Sync all mounts related to the media
1114 lockmgr(&hammer2_mntlk, LK_EXCLUSIVE);
1115 TAILQ_FOREACH(pmp, &hammer2_pfslist, mntentry) {
1116 int etmp;
1117 int i;
1119 for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
1120 if (pmp->pfs_hmps[i] != hmp)
1121 continue;
1122 etmp = hammer2_vfs_sync_pmp(pmp, MNT_WAIT);
1123 if (etmp && (error == 0 || error == ENOSPC))
1124 error = etmp;
1125 break;
1128 lockmgr(&hammer2_mntlk, LK_RELEASE);
1130 if (error && error != ENOSPC)
1131 goto failed;
1134 * If we have an ENOSPC error we have to bulkfree on the live
1135 * topology. Otherwise we can bulkfree on a snapshot.
1137 if (error) {
1138 kprintf("hammer2: WARNING! Bulkfree forced to use live "
1139 "topology due to ENOSPC\n");
1140 vchain = &hmp->vchain;
1141 hammer2_chain_ref(vchain);
1142 didsnap = 0;
1143 } else {
1144 vchain = hammer2_chain_bulksnap(hmp);
1145 didsnap = 1;
1149 * Normal bulkfree operations do not require a transaction because
1150 * they operate on a snapshot, and so can run concurrently with
1151 * any operation except another bulkfree.
1153 * If we are running bulkfree on the live topology we have to be
1154 * in a FLUSH transaction.
1156 if (didsnap == 0)
1157 hammer2_trans_init(hmp->spmp, HAMMER2_TRANS_ISFLUSH);
1159 if (bfi) {
1160 hammer2_thr_freeze(&hmp->bfthr);
1161 error = hammer2_bulkfree_pass(hmp, vchain, bfi);
1162 hammer2_thr_unfreeze(&hmp->bfthr);
1164 if (didsnap) {
1165 hammer2_chain_bulkdrop(vchain);
1166 } else {
1167 hammer2_chain_drop(vchain);
1168 hammer2_trans_done(hmp->spmp, HAMMER2_TRANS_ISFLUSH |
1169 HAMMER2_TRANS_SIDEQ);
1171 error = hammer2_error_to_errno(error);
1173 failed:
1174 lockmgr(&hmp->bflock, LK_RELEASE);
1175 return error;
1178 #if 0
1180 * Unconditionally delete meta-data in a hammer2 filesystem
1182 static
1184 hammer2_ioctl_destroy(hammer2_inode_t *ip, void *data)
1186 hammer2_ioc_destroy_t *iocd = data;
1187 hammer2_pfs_t *pmp = ip->pmp;
1188 int error;
1190 if (pmp->ronly) {
1191 error = EROFS;
1192 return error;
1195 switch(iocd->cmd) {
1196 case HAMMER2_DELETE_FILE:
1198 * Destroy a bad directory entry by name. Caller must
1199 * pass the directory as fd.
1202 hammer2_xop_unlink_t *xop;
1204 if (iocd->path[sizeof(iocd->path)-1]) {
1205 error = EINVAL;
1206 break;
1208 if (ip->meta.type != HAMMER2_OBJTYPE_DIRECTORY) {
1209 error = EINVAL;
1210 break;
1212 hammer2_pfs_memory_wait(pmp);
1213 hammer2_trans_init(pmp, 0);
1214 hammer2_inode_lock(ip, 0);
1216 xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING);
1217 hammer2_xop_setname(&xop->head, iocd->path, strlen(iocd->path));
1218 xop->isdir = -1;
1219 xop->dopermanent = H2DOPERM_PERMANENT |
1220 H2DOPERM_FORCE |
1221 H2DOPERM_IGNINO;
1222 hammer2_xop_start(&xop->head, &hammer2_unlink_desc);
1224 error = hammer2_xop_collect(&xop->head, 0);
1225 error = hammer2_error_to_errno(error);
1226 hammer2_inode_unlock(ip);
1227 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
1228 hammer2_trans_done(pmp, HAMMER2_TRANS_SIDEQ);
1230 break;
1231 case HAMMER2_DELETE_INUM:
1233 * Destroy a bad inode by inode number.
1236 hammer2_xop_lookup_t *xop;
1238 if (iocd->inum < 1) {
1239 error = EINVAL;
1240 break;
1242 hammer2_pfs_memory_wait(pmp);
1243 hammer2_trans_init(pmp, 0);
1245 xop = hammer2_xop_alloc(pmp->iroot, HAMMER2_XOP_MODIFYING);
1246 xop->lhc = iocd->inum;
1247 hammer2_xop_start(&xop->head, &hammer2_delete_desc);
1248 error = hammer2_xop_collect(&xop->head, 0);
1249 error = hammer2_error_to_errno(error);
1250 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
1251 hammer2_trans_done(pmp, HAMMER2_TRANS_SIDEQ);
1253 break;
1254 default:
1255 error = EINVAL;
1256 break;
1258 return error;
1262 * Grow a filesystem into its partition size
1264 static int
1265 hammer2_ioctl_growfs(hammer2_inode_t *ip, void *data, struct ucred *cred)
1267 hammer2_ioc_growfs_t *grow = data;
1268 hammer2_dev_t *hmp;
1269 hammer2_off_t size, delta;
1270 hammer2_tid_t mtid;
1271 struct partinfo part;
1272 struct vattr_lite va;
1273 struct buf *bp;
1274 int error;
1275 int i;
1277 hmp = ip->pmp->pfs_hmps[0];
1279 if (hmp->nvolumes > 1) {
1280 kprintf("hammer2: growfs currently unsupported "
1281 "with multiple volumes\n");
1282 return EOPNOTSUPP;
1284 KKASSERT(hmp->total_size == hmp->voldata.volu_size);
1287 * Extract from disklabel
1289 if (VOP_IOCTL(hmp->devvp, DIOCGPART, (void *)&part, 0, cred, NULL) == 0) {
1290 size = part.media_size;
1291 kprintf("hammer2: growfs partition-auto to %016jx\n",
1292 (intmax_t)size);
1293 } else if (VOP_GETATTR_LITE(hmp->devvp, &va) == 0) {
1294 size = va.va_size;
1295 kprintf("hammer2: growfs fstat-auto to %016jx\n",
1296 (intmax_t)size);
1297 } else {
1298 return EINVAL;
1302 * Expand to devvp size unless specified.
1304 grow->modified = 0;
1305 if (grow->size == 0) {
1306 grow->size = size;
1307 } else if (grow->size > size) {
1308 kprintf("hammer2: growfs size %016jx exceeds device size "
1309 "%016jx\n",
1310 (intmax_t)grow->size, (intmax_t)size);
1311 return EINVAL;
1315 * This is typically ~8MB alignment to avoid edge cases accessing
1316 * reserved blocks at the base of each 2GB zone.
1318 grow->size &= ~HAMMER2_VOLUME_ALIGNMASK64;
1319 delta = grow->size - hmp->voldata.volu_size;
1322 * Maximum allowed size is 2^63
1324 if (grow->size > 0x7FFFFFFFFFFFFFFFLU) {
1325 kprintf("hammer2: growfs failure, limit is 2^63 - 1 bytes\n");
1326 return EINVAL;
1330 * We can't shrink a filesystem
1332 if (grow->size < hmp->voldata.volu_size) {
1333 kprintf("hammer2: growfs failure, "
1334 "would shrink from %016jx to %016jx\n",
1335 (intmax_t)hmp->voldata.volu_size,
1336 (intmax_t)grow->size);
1337 return EINVAL;
1340 if (delta == 0) {
1341 kprintf("hammer2: growfs - size did not change\n");
1342 return 0;
1346 * Clear any new volume header backups that we extend into.
1347 * Skip volume headers that are already part of the filesystem.
1349 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
1350 if (i * HAMMER2_ZONE_BYTES64 < hmp->voldata.volu_size)
1351 continue;
1352 if (i * HAMMER2_ZONE_BYTES64 >= grow->size)
1353 break;
1354 kprintf("hammer2: growfs - clear volhdr %d ", i);
1355 error = bread(hmp->devvp, i * HAMMER2_ZONE_BYTES64,
1356 HAMMER2_VOLUME_BYTES, &bp);
1357 if (error) {
1358 brelse(bp);
1359 kprintf("I/O error %d\n", error);
1360 return EINVAL;
1362 bzero(bp->b_data, HAMMER2_VOLUME_BYTES);
1363 error = bwrite(bp);
1364 if (error) {
1365 kprintf("I/O error %d\n", error);
1366 return EINVAL;
1368 kprintf("\n");
1371 hammer2_trans_init(hmp->spmp, HAMMER2_TRANS_ISFLUSH);
1372 mtid = hammer2_trans_sub(hmp->spmp);
1374 kprintf("hammer2: growfs - expand by %016jx to %016jx mtid %016jx\n",
1375 (intmax_t)delta, (intmax_t)grow->size, (intmax_t)mtid);
1378 hammer2_voldata_lock(hmp);
1379 hammer2_voldata_modify(hmp);
1382 * NOTE: Just adjusting total_size for a single-volume filesystem
1383 * or for the last volume in a multi-volume filesystem, is
1384 * fine. But we can't grow any other partition in a multi-volume
1385 * filesystem. For now we just punt (at the top) on any
1386 * multi-volume filesystem.
1388 hmp->voldata.volu_size = grow->size;
1389 hmp->voldata.total_size += delta;
1390 hmp->voldata.allocator_size += delta;
1391 hmp->voldata.allocator_free += delta;
1392 hmp->total_size += delta;
1393 hmp->volumes[0].size += delta; /* note: indexes first (only) volume */
1395 hammer2_voldata_unlock(hmp);
1397 hammer2_trans_done(hmp->spmp, HAMMER2_TRANS_ISFLUSH |
1398 HAMMER2_TRANS_SIDEQ);
1399 grow->modified = 1;
1402 * Flush the mess right here and now. We could just let the
1403 * filesystem syncer do it, but this was a sensitive operation
1404 * so don't take any chances.
1406 hammer2_vfs_sync(ip->pmp->mp, MNT_WAIT);
1408 return 0;
1412 * Get a list of volumes.
1414 static int
1415 hammer2_ioctl_volume_list(hammer2_inode_t *ip, void *data)
1417 hammer2_ioc_volume_list_t *vollist = data;
1418 hammer2_ioc_volume_t entry;
1419 hammer2_volume_t *vol;
1420 hammer2_dev_t *hmp;
1421 hammer2_pfs_t *pmp;
1422 int i, error = 0, cnt = 0;
1424 pmp = ip->pmp;
1425 hmp = pmp->pfs_hmps[0];
1426 if (hmp == NULL)
1427 return (EINVAL);
1429 hammer2_voldata_lock(hmp);
1430 for (i = 0; i < hmp->nvolumes; ++i) {
1431 if (cnt >= vollist->nvolumes)
1432 break;
1433 vol = &hmp->volumes[i];
1434 bzero(&entry, sizeof(entry));
1435 /* copy hammer2_volume_t fields */
1436 entry.id = vol->id;
1437 bcopy(vol->dev->path, entry.path, sizeof(entry.path));
1438 entry.offset = vol->offset;
1439 entry.size = vol->size;
1440 error = copyout(&entry, &vollist->volumes[cnt], sizeof(entry));
1441 if (error)
1442 goto failed;
1443 cnt++;
1445 vollist->nvolumes = cnt;
1446 vollist->version = hmp->voldata.version;
1447 bcopy(pmp->pfs_names[0], vollist->pfs_name, sizeof(vollist->pfs_name));
1448 failed:
1449 hammer2_voldata_unlock(hmp);
1451 return error;
1453 #endif