usr.sbin/makefs/hammer2: Support inode timestamp in "-o R" option
[dragonfly.git] / usr.sbin / makefs / hammer2 / hammer2_ioctl.c
1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2023 Tomohiro Kusumi <tkusumi@netbsd.org>
5  * Copyright (c) 2011-2023 The DragonFly Project.  All rights reserved.
6  *
7  * This code is derived from software contributed to The DragonFly Project
8  * by Matthew Dillon <dillon@dragonflybsd.org>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
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.
23  *
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.
36  */
37 /*
38  * Ioctl Functions.
39  *
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.
43  */
44 #include <sys/diskslice.h>
45
46 #include "hammer2.h"
47 #include "makefs.h"
48
49 #if 0
50 #include <sys/kern_syscall.h>
51
52 static int hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data);
53 static int hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data);
54 static int hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data);
55 static int hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data);
56 static int hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data);
57 static int hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data);
58 static int hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data);
59 static int hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data);
60 static int hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data);
61 static int hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data);
62 static int hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data);
63 static int hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data);
64 static int hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data);
65 static int hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data);
66 static int hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data);
67 static int hammer2_ioctl_debug_dump(hammer2_inode_t *ip, u_int flags);
68 static int hammer2_ioctl_emerg_mode(hammer2_inode_t *ip, u_int mode);
69 static int hammer2_ioctl_growfs(hammer2_inode_t *ip, void *data,
70                         struct ucred *cred);
71 //static int hammer2_ioctl_inode_comp_set(hammer2_inode_t *ip, void *data);
72 //static int hammer2_ioctl_inode_comp_rec_set(hammer2_inode_t *ip, void *data);
73 //static int hammer2_ioctl_inode_comp_rec_set2(hammer2_inode_t *ip, void *data);
74 static int hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data);
75 static int hammer2_ioctl_destroy(hammer2_inode_t *ip, void *data);
76 static int hammer2_ioctl_volume_list(hammer2_inode_t *ip, void *data);
77
78 int
79 hammer2_ioctl(hammer2_inode_t *ip, u_long com, void *data, int fflag,
80               struct ucred *cred)
81 {
82         int error;
83
84         /*
85          * Standard root cred checks, will be selectively ignored below
86          * for ioctls that do not require root creds.
87          */
88         error = priv_check_cred(cred, PRIV_HAMMER_IOCTL, 0);
89
90         switch(com) {
91         case HAMMER2IOC_VERSION_GET:
92                 error = hammer2_ioctl_version_get(ip, data);
93                 break;
94         case HAMMER2IOC_RECLUSTER:
95                 if (error == 0)
96                         error = hammer2_ioctl_recluster(ip, data);
97                 break;
98         case HAMMER2IOC_REMOTE_SCAN:
99                 if (error == 0)
100                         error = hammer2_ioctl_remote_scan(ip, data);
101                 break;
102         case HAMMER2IOC_REMOTE_ADD:
103                 if (error == 0)
104                         error = hammer2_ioctl_remote_add(ip, data);
105                 break;
106         case HAMMER2IOC_REMOTE_DEL:
107                 if (error == 0)
108                         error = hammer2_ioctl_remote_del(ip, data);
109                 break;
110         case HAMMER2IOC_REMOTE_REP:
111                 if (error == 0)
112                         error = hammer2_ioctl_remote_rep(ip, data);
113                 break;
114         case HAMMER2IOC_SOCKET_GET:
115                 if (error == 0)
116                         error = hammer2_ioctl_socket_get(ip, data);
117                 break;
118         case HAMMER2IOC_SOCKET_SET:
119                 if (error == 0)
120                         error = hammer2_ioctl_socket_set(ip, data);
121                 break;
122         case HAMMER2IOC_PFS_GET:
123                 if (error == 0)
124                         error = hammer2_ioctl_pfs_get(ip, data);
125                 break;
126         case HAMMER2IOC_PFS_LOOKUP:
127                 if (error == 0)
128                         error = hammer2_ioctl_pfs_lookup(ip, data);
129                 break;
130         case HAMMER2IOC_PFS_CREATE:
131                 if (error == 0)
132                         error = hammer2_ioctl_pfs_create(ip, data);
133                 break;
134         case HAMMER2IOC_PFS_DELETE:
135                 if (error == 0)
136                         error = hammer2_ioctl_pfs_delete(ip, data);
137                 break;
138         case HAMMER2IOC_PFS_SNAPSHOT:
139                 if (error == 0)
140                         error = hammer2_ioctl_pfs_snapshot(ip, data);
141                 break;
142         case HAMMER2IOC_INODE_GET:
143                 error = hammer2_ioctl_inode_get(ip, data);
144                 break;
145         case HAMMER2IOC_INODE_SET:
146                 if (error == 0)
147                         error = hammer2_ioctl_inode_set(ip, data);
148                 break;
149         case HAMMER2IOC_BULKFREE_SCAN:
150                 error = hammer2_ioctl_bulkfree_scan(ip, data);
151                 break;
152         case HAMMER2IOC_BULKFREE_ASYNC:
153                 error = hammer2_ioctl_bulkfree_scan(ip, NULL);
154                 break;
155         case HAMMER2IOC_DESTROY:
156                 if (error == 0)
157                         error = hammer2_ioctl_destroy(ip, data);
158                 break;
159         case HAMMER2IOC_DEBUG_DUMP:
160                 error = hammer2_ioctl_debug_dump(ip, *(u_int *)data);
161                 break;
162         case HAMMER2IOC_EMERG_MODE:
163                 if (error == 0)
164                         error = hammer2_ioctl_emerg_mode(ip, *(u_int *)data);
165                 break;
166         case HAMMER2IOC_GROWFS:
167                 if (error == 0)
168                         error = hammer2_ioctl_growfs(ip, data, cred);
169                 break;
170         case HAMMER2IOC_VOLUME_LIST:
171                 if (error == 0)
172                         error = hammer2_ioctl_volume_list(ip, data);
173                 break;
174         default:
175                 error = EOPNOTSUPP;
176                 break;
177         }
178         return (error);
179 }
180 #endif
181
182 /*
183  * Retrieve version and basic info
184  */
185 int
186 hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data)
187 {
188         hammer2_ioc_version_t *version = data;
189         hammer2_dev_t *hmp;
190
191         hmp = ip->pmp->pfs_hmps[0];
192         if (hmp)
193                 version->version = hmp->voldata.version;
194         else
195                 version->version = -1;
196         return 0;
197 }
198
199 #if 0
200 static int
201 hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data)
202 {
203         hammer2_ioc_recluster_t *recl = data;
204         struct vnode *vproot;
205         struct file *fp;
206         hammer2_cluster_t *cluster;
207         int error;
208
209         fp = holdfp(curthread, recl->fd, -1);
210         if (fp) {
211                 error = VFS_ROOT(ip->pmp->mp, &vproot);
212                 if (error == 0) {
213                         cluster = &ip->pmp->iroot->cluster;
214                         kprintf("reconnect to cluster: nc=%d focus=%p\n",
215                                 cluster->nchains, cluster->focus);
216                         if (cluster->nchains != 1 || cluster->focus == NULL) {
217                                 kprintf("not a local device mount\n");
218                                 error = EINVAL;
219                         } else {
220                                 hammer2_cluster_reconnect(cluster->focus->hmp,
221                                                           fp);
222                                 kprintf("ok\n");
223                                 error = 0;
224                         }
225                         vput(vproot);
226                 }
227         } else {
228                 error = EINVAL;
229         }
230         return error;
231 }
232
233 /*
234  * Retrieve information about a remote
235  */
236 static int
237 hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data)
238 {
239         hammer2_dev_t *hmp;
240         hammer2_ioc_remote_t *remote = data;
241         int copyid = remote->copyid;
242
243         hmp = ip->pmp->pfs_hmps[0];
244         if (hmp == NULL)
245                 return (EINVAL);
246
247         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
248                 return (EINVAL);
249
250         hammer2_voldata_lock(hmp);
251         remote->copy1 = hmp->voldata.copyinfo[copyid];
252         hammer2_voldata_unlock(hmp);
253
254         /*
255          * Adjust nextid (GET only)
256          */
257         while (++copyid < HAMMER2_COPYID_COUNT &&
258                hmp->voldata.copyinfo[copyid].copyid == 0) {
259                 ;
260         }
261         if (copyid == HAMMER2_COPYID_COUNT)
262                 remote->nextid = -1;
263         else
264                 remote->nextid = copyid;
265
266         return(0);
267 }
268
269 /*
270  * Add new remote entry
271  */
272 static int
273 hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data)
274 {
275         hammer2_ioc_remote_t *remote = data;
276         hammer2_pfs_t *pmp = ip->pmp;
277         hammer2_dev_t *hmp;
278         int copyid = remote->copyid;
279         int error = 0;
280
281         hmp = pmp->pfs_hmps[0];
282         if (hmp == NULL)
283                 return (EINVAL);
284         if (copyid >= HAMMER2_COPYID_COUNT)
285                 return (EINVAL);
286
287         hammer2_voldata_lock(hmp);
288         if (copyid < 0) {
289                 for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
290                         if (hmp->voldata.copyinfo[copyid].copyid == 0)
291                                 break;
292                 }
293                 if (copyid == HAMMER2_COPYID_COUNT) {
294                         error = ENOSPC;
295                         goto failed;
296                 }
297         }
298         hammer2_voldata_modify(hmp);
299         remote->copy1.copyid = copyid;
300         hmp->voldata.copyinfo[copyid] = remote->copy1;
301         hammer2_volconf_update(hmp, copyid);
302 failed:
303         hammer2_voldata_unlock(hmp);
304         return (error);
305 }
306
307 /*
308  * Delete existing remote entry
309  */
310 static int
311 hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data)
312 {
313         hammer2_ioc_remote_t *remote = data;
314         hammer2_pfs_t *pmp = ip->pmp;
315         hammer2_dev_t *hmp;
316         int copyid = remote->copyid;
317         int error = 0;
318
319         hmp = pmp->pfs_hmps[0];
320         if (hmp == NULL)
321                 return (EINVAL);
322         if (copyid >= HAMMER2_COPYID_COUNT)
323                 return (EINVAL);
324         remote->copy1.path[sizeof(remote->copy1.path) - 1] = 0;
325         hammer2_voldata_lock(hmp);
326         if (copyid < 0) {
327                 for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
328                         if (hmp->voldata.copyinfo[copyid].copyid == 0)
329                                 continue;
330                         if (strcmp(remote->copy1.path,
331                             hmp->voldata.copyinfo[copyid].path) == 0) {
332                                 break;
333                         }
334                 }
335                 if (copyid == HAMMER2_COPYID_COUNT) {
336                         error = ENOENT;
337                         goto failed;
338                 }
339         }
340         hammer2_voldata_modify(hmp);
341         hmp->voldata.copyinfo[copyid].copyid = 0;
342         hammer2_volconf_update(hmp, copyid);
343 failed:
344         hammer2_voldata_unlock(hmp);
345         return (error);
346 }
347
348 /*
349  * Replace existing remote entry
350  */
351 static int
352 hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data)
353 {
354         hammer2_ioc_remote_t *remote = data;
355         hammer2_dev_t *hmp;
356         int copyid = remote->copyid;
357
358         hmp = ip->pmp->pfs_hmps[0];
359         if (hmp == NULL)
360                 return (EINVAL);
361         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
362                 return (EINVAL);
363
364         hammer2_voldata_lock(hmp);
365         hammer2_voldata_modify(hmp);
366         /*hammer2_volconf_update(hmp, copyid);*/
367         hammer2_voldata_unlock(hmp);
368
369         return(0);
370 }
371
372 /*
373  * Retrieve communications socket
374  */
375 static int
376 hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data)
377 {
378         return (EOPNOTSUPP);
379 }
380
381 /*
382  * Set communications socket for connection
383  */
384 static int
385 hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data)
386 {
387         hammer2_ioc_remote_t *remote = data;
388         hammer2_dev_t *hmp;
389         int copyid = remote->copyid;
390
391         hmp = ip->pmp->pfs_hmps[0];
392         if (hmp == NULL)
393                 return (EINVAL);
394         if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
395                 return (EINVAL);
396
397         hammer2_voldata_lock(hmp);
398         hammer2_voldata_unlock(hmp);
399
400         return(0);
401 }
402 #endif
403
404 /*
405  * Used to scan and retrieve PFS information.  PFS's are directories under
406  * the super-root.
407  *
408  * To scan PFSs pass name_key=0.  The function will scan for the next
409  * PFS and set all fields, as well as set name_next to the next key.
410  * When no PFSs remain, name_next is set to (hammer2_key_t)-1.
411  *
412  * To retrieve a particular PFS by key, specify the key but note that
413  * the ioctl will return the lowest key >= specified_key, so the caller
414  * must verify the key.
415  *
416  * To retrieve the PFS associated with the file descriptor, pass
417  * name_key set to (hammer2_key_t)-1.
418  */
419 int
420 hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data)
421 {
422         const hammer2_inode_data_t *ripdata;
423         hammer2_dev_t *hmp;
424         hammer2_ioc_pfs_t *pfs;
425         hammer2_chain_t *parent;
426         hammer2_chain_t *chain;
427         hammer2_key_t key_next;
428         hammer2_key_t save_key;
429         int error;
430
431         hmp = ip->pmp->pfs_hmps[0];
432         if (hmp == NULL)
433                 return (EINVAL);
434
435         pfs = data;
436         save_key = pfs->name_key;
437         error = 0;
438
439         /*
440          * Setup
441          */
442         if (save_key == (hammer2_key_t)-1) {
443                 hammer2_inode_lock(ip->pmp->iroot, 0);
444                 parent = NULL;
445                 chain = hammer2_inode_chain(ip->pmp->iroot, 0,
446                                             HAMMER2_RESOLVE_ALWAYS |
447                                             HAMMER2_RESOLVE_SHARED);
448         } else {
449                 hammer2_inode_lock(hmp->spmp->iroot, 0);
450                 parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
451                                             HAMMER2_RESOLVE_ALWAYS |
452                                             HAMMER2_RESOLVE_SHARED);
453                 chain = hammer2_chain_lookup(&parent, &key_next,
454                                             pfs->name_key, HAMMER2_KEY_MAX,
455                                             &error,
456                                             HAMMER2_LOOKUP_SHARED);
457         }
458
459         /*
460          * Locate next PFS
461          */
462         while (chain) {
463                 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE)
464                         break;
465                 if (parent == NULL) {
466                         hammer2_chain_unlock(chain);
467                         hammer2_chain_drop(chain);
468                         chain = NULL;
469                         break;
470                 }
471                 chain = hammer2_chain_next(&parent, chain, &key_next,
472                                             key_next, HAMMER2_KEY_MAX,
473                                             &error,
474                                             HAMMER2_LOOKUP_SHARED);
475         }
476         error = hammer2_error_to_errno(error);
477
478         /*
479          * Load the data being returned by the ioctl.
480          */
481         if (chain && chain->error == 0) {
482                 ripdata = &chain->data->ipdata;
483                 pfs->name_key = ripdata->meta.name_key;
484                 pfs->pfs_type = ripdata->meta.pfs_type;
485                 pfs->pfs_subtype = ripdata->meta.pfs_subtype;
486                 pfs->pfs_clid = ripdata->meta.pfs_clid;
487                 pfs->pfs_fsid = ripdata->meta.pfs_fsid;
488                 KKASSERT(ripdata->meta.name_len < sizeof(pfs->name));
489                 bcopy(ripdata->filename, pfs->name, ripdata->meta.name_len);
490                 pfs->name[ripdata->meta.name_len] = 0;
491                 ripdata = NULL; /* safety */
492
493                 /*
494                  * Calculate name_next, if any.  We are only accessing
495                  * chain->bref so we can ignore chain->error (if the key
496                  * is used later it will error then).
497                  */
498                 if (parent == NULL) {
499                         pfs->name_next = (hammer2_key_t)-1;
500                 } else {
501                         chain = hammer2_chain_next(&parent, chain, &key_next,
502                                                     key_next, HAMMER2_KEY_MAX,
503                                                     &error,
504                                                     HAMMER2_LOOKUP_SHARED);
505                         if (chain)
506                                 pfs->name_next = chain->bref.key;
507                         else
508                                 pfs->name_next = (hammer2_key_t)-1;
509                 }
510         } else {
511                 pfs->name_next = (hammer2_key_t)-1;
512                 error = ENOENT;
513         }
514
515         /*
516          * Cleanup
517          */
518         if (chain) {
519                 hammer2_chain_unlock(chain);
520                 hammer2_chain_drop(chain);
521         }
522         if (parent) {
523                 hammer2_chain_unlock(parent);
524                 hammer2_chain_drop(parent);
525         }
526         if (save_key == (hammer2_key_t)-1) {
527                 hammer2_inode_unlock(ip->pmp->iroot);
528         } else {
529                 hammer2_inode_unlock(hmp->spmp->iroot);
530         }
531
532         return (error);
533 }
534
535 /*
536  * Find a specific PFS by name
537  */
538 int
539 hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data)
540 {
541         const hammer2_inode_data_t *ripdata;
542         hammer2_dev_t *hmp;
543         hammer2_ioc_pfs_t *pfs;
544         hammer2_chain_t *parent;
545         hammer2_chain_t *chain;
546         hammer2_key_t key_next;
547         hammer2_key_t lhc;
548         int error;
549         size_t len;
550
551         hmp = ip->pmp->pfs_hmps[0];
552         if (hmp == NULL)
553                 return (EINVAL);
554
555         pfs = data;
556         error = 0;
557
558         hammer2_inode_lock(hmp->spmp->iroot, HAMMER2_RESOLVE_SHARED);
559         parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
560                                      HAMMER2_RESOLVE_ALWAYS |
561                                      HAMMER2_RESOLVE_SHARED);
562
563         pfs->name[sizeof(pfs->name) - 1] = 0;
564         len = strlen(pfs->name);
565         lhc = hammer2_dirhash(pfs->name, len);
566
567         chain = hammer2_chain_lookup(&parent, &key_next,
568                                          lhc, lhc + HAMMER2_DIRHASH_LOMASK,
569                                          &error, HAMMER2_LOOKUP_SHARED);
570         while (chain) {
571                 if (hammer2_chain_dirent_test(chain, pfs->name, len))
572                         break;
573                 chain = hammer2_chain_next(&parent, chain, &key_next,
574                                            key_next,
575                                            lhc + HAMMER2_DIRHASH_LOMASK,
576                                            &error, HAMMER2_LOOKUP_SHARED);
577         }
578         error = hammer2_error_to_errno(error);
579
580         /*
581          * Load the data being returned by the ioctl.
582          */
583         if (chain && chain->error == 0) {
584                 KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
585                 ripdata = &chain->data->ipdata;
586                 pfs->name_key = ripdata->meta.name_key;
587                 pfs->pfs_type = ripdata->meta.pfs_type;
588                 pfs->pfs_subtype = ripdata->meta.pfs_subtype;
589                 pfs->pfs_clid = ripdata->meta.pfs_clid;
590                 pfs->pfs_fsid = ripdata->meta.pfs_fsid;
591                 ripdata = NULL;
592
593                 hammer2_chain_unlock(chain);
594                 hammer2_chain_drop(chain);
595         } else if (error == 0) {
596                 error = ENOENT;
597         }
598         if (parent) {
599                 hammer2_chain_unlock(parent);
600                 hammer2_chain_drop(parent);
601         }
602         hammer2_inode_unlock(hmp->spmp->iroot);
603
604         return (error);
605 }
606
607 /*
608  * Create a new PFS under the super-root
609  */
610 int
611 hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data)
612 {
613         hammer2_inode_data_t *nipdata;
614         hammer2_chain_t *nchain;
615         hammer2_dev_t *hmp;
616         hammer2_dev_t *force_local;
617         hammer2_ioc_pfs_t *pfs;
618         hammer2_inode_t *nip;
619         hammer2_tid_t mtid;
620         int error;
621
622         hmp = ip->pmp->pfs_hmps[0];     /* XXX */
623         if (hmp == NULL)
624                 return (EINVAL);
625
626         pfs = data;
627         nip = NULL;
628
629         if (pfs->name[0] == 0)
630                 return(EINVAL);
631         pfs->name[sizeof(pfs->name) - 1] = 0;   /* ensure 0-termination */
632
633         if (hammer2_ioctl_pfs_lookup(ip, pfs) == 0)
634                 return(EEXIST);
635
636         hammer2_trans_init(hmp->spmp, HAMMER2_TRANS_ISFLUSH);
637         mtid = hammer2_trans_sub(hmp->spmp);
638         nip = hammer2_inode_create_pfs(hmp->spmp, pfs->name, strlen(pfs->name),
639                                        &error);
640         if (error == 0) {
641                 atomic_set_int(&nip->flags, HAMMER2_INODE_NOSIDEQ);
642                 hammer2_inode_modify(nip);
643                 nchain = hammer2_inode_chain(nip, 0, HAMMER2_RESOLVE_ALWAYS);
644                 error = hammer2_chain_modify(nchain, mtid, 0, 0);
645                 KKASSERT(error == 0);
646                 nipdata = &nchain->data->ipdata;
647
648                 nip->meta.pfs_type = pfs->pfs_type;
649                 nip->meta.pfs_subtype = pfs->pfs_subtype;
650                 nip->meta.pfs_clid = pfs->pfs_clid;
651                 nip->meta.pfs_fsid = pfs->pfs_fsid;
652                 nip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT;
653
654                 /*
655                  * Set default compression and check algorithm.  This
656                  * can be changed later.
657                  *
658                  * Do not allow compression on PFS's with the special name
659                  * "boot", the boot loader can't decompress (yet).
660                  */
661                 nip->meta.comp_algo =
662                         HAMMER2_ENC_ALGO(HAMMER2_COMP_NEWFS_DEFAULT);
663                 nip->meta.check_algo =
664                         HAMMER2_ENC_ALGO( HAMMER2_CHECK_XXHASH64);
665
666                 if (strcasecmp(pfs->name, "boot") == 0) {
667                         nip->meta.comp_algo =
668                                 HAMMER2_ENC_ALGO(HAMMER2_COMP_AUTOZERO);
669                 }
670
671                 /*
672                  * Super-root isn't mounted, fsync it
673                  */
674                 hammer2_chain_unlock(nchain);
675                 hammer2_inode_ref(nip);
676                 hammer2_inode_unlock(nip);
677                 hammer2_inode_chain_sync(nip);
678                 hammer2_inode_chain_flush(nip, HAMMER2_XOP_INODE_STOP |
679                                                HAMMER2_XOP_FSSYNC);
680                 hammer2_inode_drop(nip);
681                 /* nip is dead */
682
683                 /*
684                  * We still have a ref on the chain, relock and associate
685                  * with an appropriate PFS.
686                  */
687                 force_local = (hmp->hflags & HMNT2_LOCAL) ? hmp : NULL;
688
689                 hammer2_chain_lock(nchain, HAMMER2_RESOLVE_ALWAYS);
690                 nipdata = &nchain->data->ipdata;
691                 kprintf("ADD LOCAL PFS (IOCTL): %s\n", nipdata->filename);
692                 hammer2_pfsalloc(nchain, nipdata, force_local);
693
694                 hammer2_chain_unlock(nchain);
695                 hammer2_chain_drop(nchain);
696         }
697         hammer2_trans_done(hmp->spmp, HAMMER2_TRANS_ISFLUSH |
698                                       HAMMER2_TRANS_SIDEQ);
699
700         return (error);
701 }
702
703 /*
704  * Destroy an existing PFS under the super-root
705  */
706 int
707 hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
708 {
709         hammer2_ioc_pfs_t *pfs = data;
710         hammer2_dev_t   *hmp;
711         hammer2_pfs_t   *spmp;
712         hammer2_pfs_t   *pmp;
713         hammer2_xop_unlink_t *xop;
714         hammer2_inode_t *dip;
715         int error;
716         int i;
717
718         /*
719          * The PFS should be probed, so we should be able to
720          * locate it.  We only delete the PFS from the
721          * specific H2 block device (hmp), not all of
722          * them.  We must remove the PFS from the cluster
723          * before we can destroy it.
724          */
725         hmp = ip->pmp->pfs_hmps[0];
726         if (hmp == NULL)
727                 return (EINVAL);
728
729         pfs->name[sizeof(pfs->name) - 1] = 0;   /* ensure termination */
730
731         lockmgr(&hammer2_mntlk, LK_EXCLUSIVE);
732
733         TAILQ_FOREACH(pmp, &hammer2_pfslist, mntentry) {
734                 for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
735                         if (pmp->pfs_hmps[i] != hmp)
736                                 continue;
737                         if (pmp->pfs_names[i] &&
738                             strcmp(pmp->pfs_names[i], pfs->name) == 0) {
739                                 break;
740                         }
741                 }
742                 if (i != HAMMER2_MAXCLUSTER)
743                         break;
744         }
745
746         if (pmp == NULL) {
747                 lockmgr(&hammer2_mntlk, LK_RELEASE);
748                 return ENOENT;
749         }
750         if (pmp->mp) {
751                 lockmgr(&hammer2_mntlk, LK_RELEASE);
752                 return EBUSY;
753         }
754
755         /*
756          * Ok, we found the pmp and we have the index.  Permanently remove
757          * the PFS from the cluster
758          */
759         kprintf("FOUND PFS %s CLINDEX %d\n", pfs->name, i);
760         hammer2_pfsdealloc(pmp, i, 1);
761
762         lockmgr(&hammer2_mntlk, LK_RELEASE);
763
764         /*
765          * Now destroy the PFS under its device using the per-device
766          * super-root.
767          */
768         spmp = hmp->spmp;
769         dip = spmp->iroot;
770         hammer2_trans_init(spmp, 0);
771         hammer2_inode_lock(dip, 0);
772
773         xop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING);
774         hammer2_xop_setname(&xop->head, pfs->name, strlen(pfs->name));
775         xop->isdir = 2;
776         xop->dopermanent = H2DOPERM_PERMANENT | H2DOPERM_FORCE;
777         hammer2_xop_start(&xop->head, &hammer2_unlink_desc);
778
779         error = hammer2_xop_collect(&xop->head, 0);
780
781         hammer2_inode_unlock(dip);
782
783 #if 0
784         if (error == 0) {
785                 ip = hammer2_inode_get(dip->pmp, &xop->head, -1, -1);
786                 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
787                 if (ip) {
788                         hammer2_inode_unlink_finisher(ip, NULL);
789                         hammer2_inode_unlock(ip);
790                 }
791         } else {
792                 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
793         }
794 #endif
795         hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
796
797         hammer2_trans_done(spmp, HAMMER2_TRANS_SIDEQ);
798
799         return (hammer2_error_to_errno(error));
800 }
801
802 int
803 hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data)
804 {
805         hammer2_ioc_pfs_t *pfs = data;
806         hammer2_dev_t   *hmp;
807         hammer2_pfs_t   *pmp;
808         hammer2_chain_t *chain;
809         hammer2_inode_t *nip;
810         hammer2_tid_t   mtid;
811         size_t name_len;
812         int error;
813 #if 0
814         uuid_t opfs_clid;
815 #endif
816
817         if (pfs->name[0] == 0)
818                 return(EINVAL);
819         if (pfs->name[sizeof(pfs->name)-1] != 0)
820                 return(EINVAL);
821
822         pmp = ip->pmp;
823         ip = pmp->iroot;
824
825         hmp = pmp->pfs_hmps[0];
826         if (hmp == NULL)
827                 return (EINVAL);
828
829         lockmgr(&hmp->bulklk, LK_EXCLUSIVE);
830
831         /*
832          * NOSYNC is for debugging.  We skip the filesystem sync and use
833          * a normal transaction (which is less likely to stall).  used for
834          * testing filesystem consistency.
835          *
836          * In normal mode we sync the filesystem and use a flush transaction.
837          */
838         if (pfs->pfs_flags & HAMMER2_PFSFLAGS_NOSYNC) {
839                 hammer2_trans_init(pmp, 0);
840         } else {
841                 hammer2_vfs_sync(pmp->mp, MNT_WAIT);
842                 hammer2_trans_init(pmp, HAMMER2_TRANS_ISFLUSH);
843         }
844         mtid = hammer2_trans_sub(pmp);
845         hammer2_inode_lock(ip, 0);
846         hammer2_inode_modify(ip);
847         ip->meta.pfs_lsnap_tid = mtid;
848
849         /* XXX cluster it! */
850         chain = hammer2_inode_chain(ip, 0, HAMMER2_RESOLVE_ALWAYS);
851
852         name_len = strlen(pfs->name);
853         hmp = chain->hmp;
854
855         /*
856          * Create the snapshot directory under the super-root
857          *
858          * Set PFS type, generate a unique filesystem id, and generate
859          * a cluster id.  Use the same clid when snapshotting a PFS root,
860          * which theoretically allows the snapshot to be used as part of
861          * the same cluster (perhaps as a cache).
862          *
863          * Note that pfs_lsnap_tid must be set in the snapshot as well,
864          * ensuring that any nocrc/nocomp file data modifications force
865          * a copy-on-write.
866          *
867          * Copy the (flushed) blockref array.  Theoretically we could use
868          * chain_duplicate() but it becomes difficult to disentangle
869          * the shared core so for now just brute-force it.
870          */
871         hammer2_chain_unlock(chain);
872         nip = hammer2_inode_create_pfs(hmp->spmp, pfs->name, name_len, &error);
873         hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS);
874
875         if (nip) {
876                 hammer2_dev_t *force_local;
877                 hammer2_chain_t *nchain;
878                 hammer2_inode_data_t *wipdata;
879                 hammer2_tid_t starting_inum;
880
881                 atomic_set_int(&nip->flags, HAMMER2_INODE_NOSIDEQ);
882                 hammer2_inode_modify(nip);
883                 nchain = hammer2_inode_chain(nip, 0, HAMMER2_RESOLVE_ALWAYS);
884                 error = hammer2_chain_modify(nchain, mtid, 0, 0);
885                 KKASSERT(error == 0);
886                 wipdata = &nchain->data->ipdata;
887
888                 starting_inum = ip->pmp->inode_tid + 1;
889                 nip->meta.pfs_inum = starting_inum;
890                 nip->meta.pfs_type = HAMMER2_PFSTYPE_MASTER;
891                 nip->meta.pfs_subtype = HAMMER2_PFSSUBTYPE_SNAPSHOT;
892                 nip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT;
893                 nip->meta.pfs_lsnap_tid = mtid;
894                 nchain->bref.embed.stats = chain->bref.embed.stats;
895
896                 uuid_create(&nip->meta.pfs_fsid, NULL);
897
898 #if 0
899                 /*
900                  * Give the snapshot its own private cluster id.  As a
901                  * snapshot no further synchronization with the original
902                  * cluster will be done.
903                  */
904                 if (chain->flags & HAMMER2_CHAIN_PFSBOUNDARY)
905                         nip->meta.pfs_clid = opfs_clid;
906                 else
907                         uuid_create(&nip->meta.pfs_clid, NULL);
908 #endif
909                 uuid_create(&nip->meta.pfs_clid, NULL);
910                 nchain->bref.flags |= HAMMER2_BREF_FLAG_PFSROOT;
911
912                 /* XXX hack blockset copy */
913                 /* XXX doesn't work with real cluster */
914                 wipdata->meta = nip->meta;
915                 hammer2_spin_ex(&pmp->inum_spin);
916                 wipdata->u.blockset = pmp->pfs_iroot_blocksets[0];
917                 hammer2_spin_unex(&pmp->inum_spin);
918
919                 KKASSERT(wipdata == &nchain->data->ipdata);
920
921                 hammer2_chain_unlock(nchain);
922                 hammer2_inode_ref(nip);
923                 hammer2_inode_unlock(nip);
924                 hammer2_inode_chain_sync(nip);
925                 hammer2_inode_chain_flush(nip, HAMMER2_XOP_INODE_STOP |
926                                                HAMMER2_XOP_FSSYNC);
927                                                /* XXX | HAMMER2_XOP_VOLHDR */
928                 hammer2_inode_drop(nip);
929                 /* nip is dead */
930
931                 force_local = (hmp->hflags & HMNT2_LOCAL) ? hmp : NULL;
932
933                 hammer2_chain_lock(nchain, HAMMER2_RESOLVE_ALWAYS);
934                 wipdata = &nchain->data->ipdata;
935                 kprintf("SNAPSHOT LOCAL PFS (IOCTL): %s\n", wipdata->filename);
936                 hammer2_pfsalloc(nchain, wipdata, force_local);
937                 nchain->pmp->inode_tid = starting_inum;
938
939                 hammer2_chain_unlock(nchain);
940                 hammer2_chain_drop(nchain);
941         }
942
943         hammer2_chain_unlock(chain);
944         hammer2_chain_drop(chain);
945
946         hammer2_inode_unlock(ip);
947         if (pfs->pfs_flags & HAMMER2_PFSFLAGS_NOSYNC) {
948                 hammer2_trans_done(pmp, 0);
949         } else {
950                 hammer2_trans_done(pmp, HAMMER2_TRANS_ISFLUSH |
951                                         HAMMER2_TRANS_SIDEQ);
952         }
953
954         lockmgr(&hmp->bulklk, LK_RELEASE);
955
956         return (hammer2_error_to_errno(error));
957 }
958
959 /*
960  * Retrieve the raw inode structure, non-inclusive of node-specific data.
961  */
962 int
963 hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data)
964 {
965         hammer2_ioc_inode_t *ino = data;
966
967         hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
968         ino->data_count = hammer2_inode_data_count(ip);
969         ino->inode_count = hammer2_inode_inode_count(ip);
970
971         bzero(&ino->ip_data, sizeof(ino->ip_data));
972         ino->ip_data.meta = ip->meta;
973         hammer2_inode_unlock(ip);
974
975         return 0;
976 }
977
978 /*
979  * Set various parameters in an inode which cannot be set through
980  * normal filesystem VNOPS.
981  */
982 int
983 hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
984 {
985         hammer2_ioc_inode_t *ino = data;
986
987         hammer2_trans_init(ip->pmp, 0);
988         hammer2_inode_lock(ip, 0);
989
990         if ((ino->flags & HAMMER2IOC_INODE_FLAG_CHECK) &&
991             ip->meta.check_algo != ino->ip_data.meta.check_algo) {
992                 hammer2_inode_modify(ip);
993                 ip->meta.check_algo = ino->ip_data.meta.check_algo;
994         }
995         if ((ino->flags & HAMMER2IOC_INODE_FLAG_COMP) &&
996             ip->meta.comp_algo != ino->ip_data.meta.comp_algo) {
997                 hammer2_inode_modify(ip);
998                 ip->meta.comp_algo = ino->ip_data.meta.comp_algo;
999         }
1000
1001         /* Ignore these flags for now...*/
1002         if ((ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) &&
1003             ip->meta.inode_quota != ino->ip_data.meta.inode_quota) {
1004                 hammer2_inode_modify(ip);
1005                 ip->meta.inode_quota = ino->ip_data.meta.inode_quota;
1006         }
1007         if ((ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) &&
1008             ip->meta.data_quota != ino->ip_data.meta.data_quota) {
1009                 hammer2_inode_modify(ip);
1010                 ip->meta.data_quota = ino->ip_data.meta.data_quota;
1011         }
1012         if ((ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) &&
1013             ip->meta.ncopies != ino->ip_data.meta.ncopies) {
1014                 hammer2_inode_modify(ip);
1015                 ip->meta.ncopies = ino->ip_data.meta.ncopies;
1016         }
1017         hammer2_inode_unlock(ip);
1018         hammer2_trans_done(ip->pmp, HAMMER2_TRANS_SIDEQ);
1019
1020         return (0);
1021 }
1022
1023 #if 0
1024 static
1025 int
1026 hammer2_ioctl_debug_dump(hammer2_inode_t *ip, u_int flags)
1027 {
1028         hammer2_chain_t *chain;
1029         int count = 100000;
1030         int i;
1031
1032         for (i = 0; i < ip->cluster.nchains; ++i) {
1033                 chain = ip->cluster.array[i].chain;
1034                 if (chain == NULL)
1035                         continue;
1036                 kprintf("cluster #%d\n", i);
1037                 hammer2_dump_chain(chain, 0, 0, &count, 'i', flags);
1038         }
1039         return 0;
1040 }
1041 #endif
1042
1043 /*
1044  * Turn on or off emergency mode on a filesystem.
1045  */
1046 int
1047 hammer2_ioctl_emerg_mode(hammer2_inode_t *ip, u_int mode)
1048 {
1049         hammer2_pfs_t *pmp;
1050         hammer2_dev_t *hmp;
1051         int i;
1052
1053         pmp = ip->pmp;
1054         if (mode) {
1055                 kprintf("hammer2: WARNING: Emergency mode enabled\n");
1056                 atomic_set_int(&pmp->flags, HAMMER2_PMPF_EMERG);
1057         } else {
1058                 kprintf("hammer2: WARNING: Emergency mode disabled\n");
1059                 atomic_clear_int(&pmp->flags, HAMMER2_PMPF_EMERG);
1060         }
1061         for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
1062                 hmp = pmp->pfs_hmps[i];
1063                 if (hmp == NULL)
1064                         continue;
1065                 if (mode)
1066                         atomic_set_int(&hmp->hflags, HMNT2_EMERG);
1067                 else
1068                         atomic_clear_int(&hmp->hflags, HMNT2_EMERG);
1069         }
1070         return 0;
1071 }
1072
1073 /*
1074  * Do a bulkfree scan on media related to the PFS.  This routine will
1075  * flush all PFSs associated with the media before doing the bulkfree
1076  * scan.
1077  *
1078  * This version can only run on non-clustered media.  A new ioctl or a
1079  * temporary mount of @LOCAL will be needed to run on clustered media.
1080  */
1081 int
1082 hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data)
1083 {
1084         hammer2_ioc_bulkfree_t *bfi = data;
1085         hammer2_dev_t   *hmp;
1086         hammer2_pfs_t   *pmp;
1087         hammer2_chain_t *vchain;
1088         int error;
1089         int didsnap;
1090
1091         pmp = ip->pmp;
1092         ip = pmp->iroot;
1093
1094         hmp = pmp->pfs_hmps[0];
1095         if (hmp == NULL)
1096                 return (EINVAL);
1097         if (bfi == NULL)
1098                 return (EINVAL);
1099
1100         /*
1101          * Bulkfree has to be serialized to guarantee at least one sync
1102          * inbetween bulkfrees.
1103          */
1104         error = lockmgr(&hmp->bflock, LK_EXCLUSIVE | LK_PCATCH);
1105         if (error)
1106                 return error;
1107
1108         /*
1109          * Sync all mounts related to the media
1110          */
1111         lockmgr(&hammer2_mntlk, LK_EXCLUSIVE);
1112         TAILQ_FOREACH(pmp, &hammer2_pfslist, mntentry) {
1113                 int etmp;
1114                 int i;
1115
1116                 for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
1117                         if (pmp->pfs_hmps[i] != hmp)
1118                                 continue;
1119                         etmp = hammer2_vfs_sync_pmp(pmp, MNT_WAIT);
1120                         if (etmp && (error == 0 || error == ENOSPC))
1121                                 error = etmp;
1122                         break;
1123                 }
1124         }
1125         lockmgr(&hammer2_mntlk, LK_RELEASE);
1126
1127         if (error && error != ENOSPC)
1128                 goto failed;
1129
1130         /*
1131          * If we have an ENOSPC error we have to bulkfree on the live
1132          * topology.  Otherwise we can bulkfree on a snapshot.
1133          */
1134         if (error) {
1135                 kprintf("hammer2: WARNING! Bulkfree forced to use live "
1136                         "topology due to ENOSPC\n");
1137                 vchain = &hmp->vchain;
1138                 hammer2_chain_ref(vchain);
1139                 didsnap = 0;
1140         } else {
1141                 vchain = hammer2_chain_bulksnap(hmp);
1142                 didsnap = 1;
1143         }
1144
1145         /*
1146          * Normal bulkfree operations do not require a transaction because
1147          * they operate on a snapshot, and so can run concurrently with
1148          * any operation except another bulkfree.
1149          *
1150          * If we are running bulkfree on the live topology we have to be
1151          * in a FLUSH transaction.
1152          */
1153         if (didsnap == 0)
1154                 hammer2_trans_init(hmp->spmp, HAMMER2_TRANS_ISFLUSH);
1155
1156         if (bfi) {
1157                 hammer2_thr_freeze(&hmp->bfthr);
1158                 error = hammer2_bulkfree_pass(hmp, vchain, bfi);
1159                 hammer2_thr_unfreeze(&hmp->bfthr);
1160         }
1161         if (didsnap) {
1162                 hammer2_chain_bulkdrop(vchain);
1163         } else {
1164                 hammer2_chain_drop(vchain);
1165                 hammer2_trans_done(hmp->spmp, HAMMER2_TRANS_ISFLUSH |
1166                                         HAMMER2_TRANS_SIDEQ);
1167         }
1168         error = hammer2_error_to_errno(error);
1169
1170 failed:
1171         lockmgr(&hmp->bflock, LK_RELEASE);
1172         return error;
1173 }
1174
1175 /*
1176  * Unconditionally delete meta-data in a hammer2 filesystem
1177  */
1178 int
1179 hammer2_ioctl_destroy(hammer2_inode_t *ip, void *data)
1180 {
1181         hammer2_ioc_destroy_t *iocd = data;
1182         hammer2_pfs_t *pmp = ip->pmp;
1183         int error;
1184
1185         if (pmp->ronly) {
1186                 error = EROFS;
1187                 return error;
1188         }
1189
1190         switch(iocd->cmd) {
1191         case HAMMER2_DELETE_FILE:
1192                 /*
1193                  * Destroy a bad directory entry by name.  Caller must
1194                  * pass the directory as fd.
1195                  */
1196                 {
1197                 hammer2_xop_unlink_t *xop;
1198
1199                 if (iocd->path[sizeof(iocd->path)-1]) {
1200                         error = EINVAL;
1201                         break;
1202                 }
1203                 if (ip->meta.type != HAMMER2_OBJTYPE_DIRECTORY) {
1204                         error = EINVAL;
1205                         break;
1206                 }
1207                 hammer2_pfs_memory_wait(pmp);
1208                 hammer2_trans_init(pmp, 0);
1209                 hammer2_inode_lock(ip, 0);
1210
1211                 xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING);
1212                 hammer2_xop_setname(&xop->head, iocd->path, strlen(iocd->path));
1213                 xop->isdir = -1;
1214                 xop->dopermanent = H2DOPERM_PERMANENT |
1215                                    H2DOPERM_FORCE |
1216                                    H2DOPERM_IGNINO;
1217                 hammer2_xop_start(&xop->head, &hammer2_unlink_desc);
1218
1219                 error = hammer2_xop_collect(&xop->head, 0);
1220                 error = hammer2_error_to_errno(error);
1221                 hammer2_inode_unlock(ip);
1222                 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
1223                 hammer2_trans_done(pmp, HAMMER2_TRANS_SIDEQ);
1224                 }
1225                 break;
1226         case HAMMER2_DELETE_INUM:
1227                 /*
1228                  * Destroy a bad inode by inode number.
1229                  */
1230                 {
1231                 hammer2_xop_lookup_t *xop;
1232
1233                 if (iocd->inum < 1) {
1234                         error = EINVAL;
1235                         break;
1236                 }
1237                 hammer2_pfs_memory_wait(pmp);
1238                 hammer2_trans_init(pmp, 0);
1239
1240                 xop = hammer2_xop_alloc(pmp->iroot, HAMMER2_XOP_MODIFYING);
1241                 xop->lhc = iocd->inum;
1242                 hammer2_xop_start(&xop->head, &hammer2_delete_desc);
1243                 error = hammer2_xop_collect(&xop->head, 0);
1244                 error = hammer2_error_to_errno(error);
1245                 hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
1246                 hammer2_trans_done(pmp, HAMMER2_TRANS_SIDEQ);
1247                 }
1248                 break;
1249         default:
1250                 error = EINVAL;
1251                 break;
1252         }
1253         return error;
1254 }
1255
1256 /*
1257  * Grow a filesystem into its partition size
1258  */
1259 int
1260 hammer2_ioctl_growfs(hammer2_inode_t *ip, void *data, struct ucred *cred)
1261 {
1262         hammer2_ioc_growfs_t *grow = data;
1263         hammer2_dev_t *hmp;
1264         hammer2_off_t size, delta;
1265         hammer2_tid_t mtid;
1266         struct partinfo part;
1267         struct stat st;
1268         struct m_buf *bp;
1269         int error;
1270         int i;
1271
1272         hmp = ip->pmp->pfs_hmps[0];
1273
1274         if (hmp->nvolumes > 1) {
1275                 kprintf("hammer2: growfs currently unsupported "
1276                         "with multiple volumes\n");
1277                 return EOPNOTSUPP;
1278         }
1279         KKASSERT(hmp->total_size == hmp->voldata.volu_size);
1280
1281         /*
1282          * Extract from disklabel
1283          */
1284         if (ioctl(hmp->devvp->fs->fd, DIOCGPART, &part) == 0) {
1285                 size = part.media_size;
1286                 kprintf("hammer2: growfs partition-auto to %016jx\n",
1287                         (intmax_t)size);
1288         } else if (fstat(hmp->devvp->fs->fd, &st) == 0) {
1289                 size = st.st_size;
1290                 kprintf("hammer2: growfs fstat-auto to %016jx\n",
1291                         (intmax_t)size);
1292         } else {
1293                 return EINVAL;
1294         }
1295
1296         /*
1297          * Expand to devvp size unless specified.
1298          */
1299         grow->modified = 0;
1300         if (grow->size == 0) {
1301                 grow->size = size;
1302         } else if (grow->size > size) {
1303                 kprintf("hammer2: growfs size %016jx exceeds device size "
1304                         "%016jx\n",
1305                         (intmax_t)grow->size, (intmax_t)size);
1306                 return EINVAL;
1307         }
1308
1309         /*
1310          * This is typically ~8MB alignment to avoid edge cases accessing
1311          * reserved blocks at the base of each 2GB zone.
1312          */
1313         grow->size &= ~HAMMER2_VOLUME_ALIGNMASK64;
1314         delta = grow->size - hmp->voldata.volu_size;
1315
1316         /*
1317          * Maximum allowed size is 2^63
1318          */
1319         if (grow->size > 0x7FFFFFFFFFFFFFFFLU) {
1320                 kprintf("hammer2: growfs failure, limit is 2^63 - 1 bytes\n");
1321                 return EINVAL;
1322         }
1323
1324         /*
1325          * We can't shrink a filesystem
1326          */
1327         if (grow->size < hmp->voldata.volu_size) {
1328                 kprintf("hammer2: growfs failure, "
1329                         "would shrink from %016jx to %016jx\n",
1330                         (intmax_t)hmp->voldata.volu_size,
1331                         (intmax_t)grow->size);
1332                 return EINVAL;
1333         }
1334
1335         if (delta == 0) {
1336                 kprintf("hammer2: growfs - size did not change\n");
1337                 return 0;
1338         }
1339
1340         /*
1341          * Clear any new volume header backups that we extend into.
1342          * Skip volume headers that are already part of the filesystem.
1343          */
1344         for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
1345                 if (i * HAMMER2_ZONE_BYTES64 < hmp->voldata.volu_size)
1346                         continue;
1347                 if (i * HAMMER2_ZONE_BYTES64 >= grow->size)
1348                         break;
1349                 kprintf("hammer2: growfs - clear volhdr %d ", i);
1350                 error = breadx(hmp->devvp, i * HAMMER2_ZONE_BYTES64,
1351                               HAMMER2_VOLUME_BYTES, &bp);
1352                 if (error) {
1353                         brelse(bp);
1354                         kprintf("I/O error %d\n", error);
1355                         return EINVAL;
1356                 }
1357                 bzero(bp->b_data, HAMMER2_VOLUME_BYTES);
1358                 error = bwrite(bp);
1359                 if (error) {
1360                         kprintf("I/O error %d\n", error);
1361                         return EINVAL;
1362                 }
1363                 kprintf("\n");
1364         }
1365
1366         hammer2_trans_init(hmp->spmp, HAMMER2_TRANS_ISFLUSH);
1367         mtid = hammer2_trans_sub(hmp->spmp);
1368
1369         kprintf("hammer2: growfs - expand by %016jx to %016jx mtid %016jx\n",
1370                 (intmax_t)delta, (intmax_t)grow->size, (intmax_t)mtid);
1371
1372
1373         hammer2_voldata_lock(hmp);
1374         hammer2_voldata_modify(hmp);
1375
1376         /*
1377          * NOTE: Just adjusting total_size for a single-volume filesystem
1378          *       or for the last volume in a multi-volume filesystem, is
1379          *       fine.  But we can't grow any other partition in a multi-volume
1380          *       filesystem.  For now we just punt (at the top) on any
1381          *       multi-volume filesystem.
1382          */
1383         hmp->voldata.volu_size = grow->size;
1384         hmp->voldata.total_size += delta;
1385         hmp->voldata.allocator_size += delta;
1386         hmp->voldata.allocator_free += delta;
1387         hmp->total_size += delta;
1388         hmp->volumes[0].size += delta;  /* note: indexes first (only) volume */
1389
1390         hammer2_voldata_unlock(hmp);
1391
1392         hammer2_trans_done(hmp->spmp, HAMMER2_TRANS_ISFLUSH |
1393                                       HAMMER2_TRANS_SIDEQ);
1394         grow->modified = 1;
1395
1396         /*
1397          * Flush the mess right here and now.  We could just let the
1398          * filesystem syncer do it, but this was a sensitive operation
1399          * so don't take any chances.
1400          */
1401         hammer2_vfs_sync(ip->pmp->mp, MNT_WAIT);
1402
1403         return 0;
1404 }
1405
1406 #if 0
1407 /*
1408  * Get a list of volumes.
1409  */
1410 static int
1411 hammer2_ioctl_volume_list(hammer2_inode_t *ip, void *data)
1412 {
1413         hammer2_ioc_volume_list_t *vollist = data;
1414         hammer2_ioc_volume_t entry;
1415         hammer2_volume_t *vol;
1416         hammer2_dev_t *hmp;
1417         hammer2_pfs_t *pmp;
1418         int i, error = 0, cnt = 0;
1419
1420         pmp = ip->pmp;
1421         hmp = pmp->pfs_hmps[0];
1422         if (hmp == NULL)
1423                 return (EINVAL);
1424
1425         hammer2_voldata_lock(hmp);
1426         for (i = 0; i < hmp->nvolumes; ++i) {
1427                 if (cnt >= vollist->nvolumes)
1428                         break;
1429                 vol = &hmp->volumes[i];
1430                 bzero(&entry, sizeof(entry));
1431                 /* copy hammer2_volume_t fields */
1432                 entry.id = vol->id;
1433                 bcopy(vol->dev->path, entry.path, sizeof(entry.path));
1434                 entry.offset = vol->offset;
1435                 entry.size = vol->size;
1436                 error = copyout(&entry, &vollist->volumes[cnt], sizeof(entry));
1437                 if (error)
1438                         goto failed;
1439                 cnt++;
1440         }
1441         vollist->nvolumes = cnt;
1442         vollist->version = hmp->voldata.version;
1443         bcopy(pmp->pfs_names[0], vollist->pfs_name, sizeof(vollist->pfs_name));
1444 failed:
1445         hammer2_voldata_unlock(hmp);
1446
1447         return error;
1448 }
1449 #endif