Expand HAMMER filesystem step 1/2
[dragonfly.git] / sys / vfs / hammer / hammer_expand.c
1 /*
2  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com> and
6  * Michael Neumann <mneumann@ntecs.de>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36
37 #include "hammer.h"
38 #include <sys/fcntl.h>
39 #include <sys/nlookup.h>
40 #include <sys/buf.h>
41
42 static int
43 hammer_format_volume_header(struct hammer_mount *hmp, const char *vol_name,
44         int vol_no, int vol_count,
45         int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size);
46
47
48 int
49 hammer_ioc_expand(hammer_transaction_t trans, hammer_inode_t ip,
50                 struct hammer_ioc_expand *expand)
51 {
52         struct hammer_mount *hmp = trans->hmp;
53         struct mount *mp = hmp->mp;
54         int error;
55
56         if (mp->mnt_flag & MNT_RDONLY) {
57                 kprintf("Cannot expand read-only HAMMER filesystem\n");
58                 return (EINVAL);
59         }
60
61         if (hmp->nvolumes + 1 >= HAMMER_MAX_VOLUMES) {
62                 kprintf("Max number of HAMMER volumes exceeded\n");
63                 return (EINVAL);
64         }
65
66         error = hammer_format_volume_header(
67                 hmp,
68                 hmp->rootvol->ondisk->vol_name,
69                 hmp->nvolumes,
70                 hmp->nvolumes+1,
71                 expand->vol_size,
72                 expand->boot_area_size,
73                 expand->mem_area_size);
74
75         if (error == 0) {
76                 error = hammer_install_volume(hmp, expand->device_name, NULL);
77         }
78
79         if (error == 0) {
80                 ++hmp->nvolumes;
81                 hammer_sync_lock_sh(trans);
82                 hammer_lock_ex(&hmp->blkmap_lock);
83
84                 /*
85                  * Set each volumes new value of the vol_count field.
86                  */
87                 for (int vol_no = 0; vol_no < hmp->nvolumes; ++vol_no) {
88                         hammer_volume_t volume;
89                         volume = hammer_get_volume(hmp, vol_no, &error);
90                         KKASSERT(error == 0);
91                         hammer_modify_volume_field(trans, volume, vol_count);
92                         volume->ondisk->vol_count = hmp->nvolumes;
93                         hammer_modify_volume_done(volume);
94                         hammer_rel_volume(volume, 0);
95                 }
96
97                 hammer_unlock(&hmp->blkmap_lock);
98                 hammer_sync_unlock(trans);
99         }
100
101         if (error) {
102                 kprintf("An error occured: %d\n", error);
103         }
104
105         return (error);
106 }
107
108 static int
109 hammer_format_volume_header(struct hammer_mount *hmp, const char *vol_name,
110         int vol_no, int vol_count,
111         int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size)
112 {
113         struct vnode *devvp = NULL;
114         struct buf *bp = NULL;
115         struct nlookupdata nd;
116         struct hammer_volume_ondisk *ondisk;
117         int error;
118
119         /*
120          * Get the device vnode
121          */
122         error = nlookup_init(&nd, vol_name, UIO_SYSSPACE, NLC_FOLLOW);
123         if (error == 0)
124                 error = nlookup(&nd);
125         if (error == 0)
126                 error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
127         nlookup_done(&nd);
128
129         if (error == 0) {
130                 if (vn_isdisk(devvp, &error)) {
131                         error = vfs_mountedon(devvp);
132                 }
133         }
134         if (error == 0 &&
135             count_udev(devvp->v_umajor, devvp->v_uminor) > 0) {
136                 error = EBUSY;
137         }
138         if (error == 0) {
139                 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
140                 error = vinvalbuf(devvp, V_SAVE, 0, 0);
141                 if (error == 0) {
142                         error = VOP_OPEN(devvp, FREAD|FWRITE, FSCRED, NULL);
143                 }
144                 vn_unlock(devvp);
145         }
146         if (error) {
147                 if (devvp)
148                         vrele(devvp);
149                 return (error);
150         }
151
152         /*
153          * Extract the volume number from the volume header and do various
154          * sanity checks.
155          */
156         KKASSERT(HAMMER_BUFSIZE >= sizeof(struct hammer_volume_ondisk));
157         error = bread(devvp, 0LL, HAMMER_BUFSIZE, &bp);
158         if (error || bp->b_bcount < sizeof(struct hammer_volume_ondisk))
159                 goto late_failure;
160
161         ondisk = (struct hammer_volume_ondisk*) bp->b_data;
162
163         /*
164          * Note that we do NOT allow to use a device that contains
165          * a valid HAMMER signature. It has to be cleaned up with dd
166          * before.
167          */
168         if (ondisk->vol_signature == HAMMER_FSBUF_VOLUME) {
169                 kprintf("hammer_expand: Formatting of valid HAMMER volume "
170                         "%s denied. Erase with dd!\n", vol_name);
171                 error = EFTYPE;
172                 goto late_failure;
173         }
174
175         bzero(ondisk, sizeof(struct hammer_volume_ondisk));
176         ksnprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", vol_name);
177         ondisk->vol_fstype = hmp->rootvol->ondisk->vol_fstype;
178         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
179         ondisk->vol_fsid = hmp->fsid;
180         ondisk->vol_rootvol = hmp->rootvol->vol_no;
181         ondisk->vol_no = vol_no;
182         ondisk->vol_count = vol_count;
183         ondisk->vol_version = hmp->version;
184
185         /*
186          * Reserve space for (future) header junk, setup our poor-man's
187          * bigblock allocator.
188          */
189         int64_t vol_alloc = HAMMER_BUFSIZE * 16;
190
191         ondisk->vol_bot_beg = vol_alloc;
192         vol_alloc += boot_area_size;
193         ondisk->vol_mem_beg = vol_alloc;
194         vol_alloc += mem_area_size;
195
196         /*
197          * The remaining area is the zone 2 buffer allocation area.  These
198          * buffers
199          */
200         ondisk->vol_buf_beg = vol_alloc;
201         ondisk->vol_buf_end = vol_size & ~(int64_t)HAMMER_BUFMASK;
202
203         if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
204                 kprintf("volume %d %s is too small to hold the volume header",
205                      ondisk->vol_no, ondisk->vol_name);
206                 error = EFTYPE;
207                 goto late_failure;
208         }
209
210         ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
211                               HAMMER_BUFSIZE;
212         ondisk->vol_blocksize = HAMMER_BUFSIZE;
213
214         /*
215          * Write volume header to disk
216          */
217         error = bwrite(bp);
218         bp = NULL;
219
220 late_failure:
221         if (bp)
222                 brelse(bp);
223         VOP_CLOSE(devvp, FREAD|FWRITE);
224         if (devvp)
225                 vrele(devvp);
226         return (error);
227 }