Expand HAMMER filesystem step 1/2
[dragonfly.git] / sys / vfs / hammer / hammer_expand.c
CommitLineData
e27700cf
MN
1/*
2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
90ecab35
MN
5 * by Matthew Dillon <dillon@backplane.com> and
6 * Michael Neumann <mneumann@ntecs.de>
e27700cf
MN
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"
90ecab35
MN
38#include <sys/fcntl.h>
39#include <sys/nlookup.h>
40#include <sys/buf.h>
41
42static int
43hammer_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
e27700cf
MN
47
48int
49hammer_ioc_expand(hammer_transaction_t trans, hammer_inode_t ip,
50 struct hammer_ioc_expand *expand)
51{
90ecab35
MN
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
108static int
109hammer_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
220late_failure:
221 if (bp)
222 brelse(bp);
223 VOP_CLOSE(devvp, FREAD|FWRITE);
224 if (devvp)
225 vrele(devvp);
226 return (error);
e27700cf 227}