HAMMER 38D/Many: Undo/Synchronization and crash recovery
[dfdiff.git] / sys / vfs / hammer / hammer_recover.c
bloba410416e7f00afe468a69fafc0e58af2ed59f3a6
1 /*
2 * Copyright (c) 2008 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>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 * $DragonFly: src/sys/vfs/hammer/hammer_recover.c,v 1.10 2008/04/26 02:54:00 dillon Exp $
37 #include "hammer.h"
39 static int hammer_check_tail_signature(hammer_fifo_tail_t tail,
40 hammer_off_t end_off);
41 static void hammer_recover_copy_undo(hammer_off_t undo_offset,
42 char *src, char *dst, int bytes);
43 static void hammer_recover_debug_dump(int w, char *buf, int bytes);
44 static int hammer_recover_undo(hammer_mount_t hmp, hammer_fifo_undo_t undo,
45 int bytes);
48 * Recover a filesystem on mount
50 int
51 hammer_recover(hammer_mount_t hmp, hammer_volume_t root_volume)
53 hammer_blockmap_t rootmap;
54 hammer_buffer_t buffer;
55 hammer_off_t scan_offset;
56 hammer_off_t bytes;
57 hammer_fifo_tail_t tail;
58 hammer_fifo_undo_t undo;
59 int error;
62 * Examine the UNDO FIFO. If it is empty the filesystem is clean
63 * and no action need be taken.
65 rootmap = &root_volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
66 if (rootmap->first_offset == rootmap->next_offset)
67 return(0);
69 if (rootmap->next_offset < rootmap->first_offset)
70 bytes = rootmap->alloc_offset - rootmap->first_offset +
71 rootmap->next_offset;
72 bytes = (rootmap->next_offset - rootmap->first_offset);
73 kprintf("HAMMER(%s) Start Recovery (%lld bytes of UNDO)\n",
74 root_volume->ondisk->vol_name, bytes);
77 * Scan the UNDOs backwards.
79 scan_offset = rootmap->next_offset;
80 buffer = NULL;
81 if (scan_offset > rootmap->alloc_offset) {
82 kprintf("HAMMER(%s) UNDO record at %016llx FIFO overflow\n",
83 root_volume->ondisk->vol_name,
84 scan_offset);
85 error = EIO;
86 goto failed;
89 while ((int64_t)bytes > 0) {
90 kprintf("scan_offset %016llx\n", scan_offset);
91 if (scan_offset - sizeof(*tail) <
92 HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0)) {
93 kprintf("HAMMER(%s) UNDO record at %016llx FIFO "
94 "underflow\n",
95 root_volume->ondisk->vol_name,
96 scan_offset);
97 error = EIO;
98 break;
100 if (scan_offset == HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0)) {
101 scan_offset = rootmap->alloc_offset;
102 continue;
104 tail = hammer_bread(hmp, scan_offset - sizeof(*tail),
105 &error, &buffer);
106 if (error) {
107 kprintf("HAMMER(%s) Unable to read UNDO TAIL "
108 "at %016llx\n",
109 root_volume->ondisk->vol_name,
110 scan_offset - sizeof(*tail));
111 break;
114 if (hammer_check_tail_signature(tail, scan_offset) != 0) {
115 kprintf("HAMMER(%s) Illegal UNDO TAIL signature "
116 "at %016llx\n",
117 root_volume->ondisk->vol_name,
118 scan_offset - sizeof(*tail));
119 error = EIO;
120 break;
122 undo = (void *)((char *)tail + sizeof(*tail) - tail->tail_size);
124 error = hammer_recover_undo(hmp, undo,
125 HAMMER_BUFSIZE -
126 (int)((char *)undo - (char *)buffer->ondisk));
127 if (error) {
128 kprintf("HAMMER(%s) UNDO record at %016llx failed\n",
129 root_volume->ondisk->vol_name,
130 scan_offset - tail->tail_size);
131 break;
133 scan_offset -= tail->tail_size;
134 bytes -= tail->tail_size;
136 failed:
137 if (buffer)
138 hammer_rel_buffer(buffer, 0);
139 return (error);
142 static int
143 hammer_check_tail_signature(hammer_fifo_tail_t tail, hammer_off_t end_off)
145 int max_bytes;
147 max_bytes = ((end_off - sizeof(*tail)) & HAMMER_BUFMASK);
148 max_bytes += sizeof(*tail);
151 * tail overlaps buffer boundary
153 if (((end_off - sizeof(*tail)) ^ (end_off - 1)) & ~HAMMER_BUFMASK64) {
154 return(1);
158 * signature check, the tail signature is allowed to be the head
159 * signature only for 8-byte PADs.
161 switch(tail->tail_signature) {
162 case HAMMER_TAIL_SIGNATURE:
163 break;
164 case HAMMER_HEAD_SIGNATURE:
165 if (tail->tail_type != HAMMER_HEAD_TYPE_PAD ||
166 tail->tail_size != sizeof(*tail)) {
167 return(2);
169 break;
173 * The undo structure must not overlap a buffer boundary.
175 if (tail->tail_size < 0 || tail->tail_size > max_bytes) {
176 return(3);
178 return(0);
181 static int
182 hammer_recover_undo(hammer_mount_t hmp, hammer_fifo_undo_t undo, int bytes)
184 hammer_fifo_tail_t tail;
185 hammer_volume_t volume;
186 hammer_buffer_t buffer;
187 int zone;
188 int error;
189 int vol_no;
190 int max_bytes;
191 u_int32_t offset;
194 * Basic sanity checks
196 if (bytes < HAMMER_HEAD_ALIGN) {
197 kprintf("HAMMER: Undo alignment error (%d)\n", bytes);
198 return(EIO);
200 if (undo->head.hdr_signature != HAMMER_HEAD_SIGNATURE) {
201 kprintf("HAMMER: Bad head signature %04x\n",
202 undo->head.hdr_signature);
203 return(EIO);
205 if (undo->head.hdr_size < HAMMER_HEAD_ALIGN ||
206 undo->head.hdr_size > bytes) {
207 kprintf("HAMMER: Bad size %d\n", bytes);
208 return(EIO);
212 * Skip PAD records. Note that PAD records also do not require
213 * a tail.
215 if (undo->head.hdr_type == HAMMER_HEAD_TYPE_PAD)
216 return(0);
219 * Check the tail
221 bytes = undo->head.hdr_size;
222 tail = (void *)((char *)undo + bytes - sizeof(*tail));
223 if (tail->tail_size != undo->head.hdr_size) {
224 kprintf("HAMMER: Bad tail size %d\n", tail->tail_size);
225 return(EIO);
227 if (tail->tail_type != undo->head.hdr_type) {
228 kprintf("HAMMER: Bad tail type %d\n", tail->tail_type);
229 return(EIO);
233 * Only process UNDO records
235 if (undo->head.hdr_type != HAMMER_HEAD_TYPE_UNDO)
236 return(0);
239 * Validate the UNDO record.
241 max_bytes = undo->head.hdr_size - sizeof(*undo) - sizeof(*tail);
242 if (undo->undo_data_bytes < 0 || undo->undo_data_bytes > max_bytes) {
243 kprintf("HAMMER: Corrupt UNDO record, undo_data_bytes %d/%d\n",
244 undo->undo_data_bytes, max_bytes);
245 return(EIO);
249 * The undo offset may only be a zone-1 or zone-2 offset.
251 * Currently we only support a zone-1 offset representing the
252 * volume header.
254 zone = HAMMER_ZONE_DECODE(undo->undo_offset);
255 offset = undo->undo_offset & HAMMER_BUFMASK;
257 if (offset + undo->undo_data_bytes > HAMMER_BUFSIZE) {
258 kprintf("HAMMER: Corrupt UNDO record, bad offset\n");
259 return (EIO);
262 switch(zone) {
263 case HAMMER_ZONE_RAW_VOLUME_INDEX:
264 vol_no = HAMMER_VOL_DECODE(undo->undo_offset);
265 volume = hammer_get_volume(hmp, vol_no, &error);
266 if (volume == NULL) {
267 kprintf("HAMMER: UNDO record, "
268 "cannot access volume %d\n", vol_no);
269 break;
271 hammer_modify_volume(NULL, volume, NULL, 0);
272 hammer_recover_copy_undo(undo->undo_offset,
273 (char *)(undo + 1),
274 (char *)volume->ondisk + offset,
275 undo->undo_data_bytes);
276 hammer_modify_volume_done(volume);
277 hammer_io_flush(&volume->io);
278 hammer_rel_volume(volume, 0);
279 break;
280 case HAMMER_ZONE_RAW_BUFFER_INDEX:
281 buffer = hammer_get_buffer(hmp, undo->undo_offset, 0, &error);
282 if (buffer == NULL) {
283 kprintf("HAMMER: UNDO record, "
284 "cannot access buffer %016llx\n",
285 undo->undo_offset);
286 break;
288 hammer_modify_buffer(NULL, buffer, NULL, 0);
289 hammer_recover_copy_undo(undo->undo_offset,
290 (char *)(undo + 1),
291 (char *)buffer->ondisk + offset,
292 undo->undo_data_bytes);
293 hammer_modify_buffer_done(buffer);
294 hammer_io_flush(&buffer->io);
295 hammer_rel_buffer(buffer, 0);
296 break;
297 default:
298 kprintf("HAMMER: Corrupt UNDO record\n");
299 error = EIO;
301 return (error);
304 static void
305 hammer_recover_copy_undo(hammer_off_t undo_offset,
306 char *src, char *dst, int bytes)
308 kprintf("UNDO %016llx:", undo_offset);
309 hammer_recover_debug_dump(22, dst, bytes);
310 kprintf("%22s", "to:");
311 hammer_recover_debug_dump(22, src, bytes);
312 bcopy(src, dst, bytes);
315 static void
316 hammer_recover_debug_dump(int w, char *buf, int bytes)
318 int i;
320 for (i = 0; i < bytes; ++i) {
321 if (i && (i & 15) == 0)
322 kprintf("\n%*.*s", w, w, "");
323 kprintf(" %02x", (unsigned char)buf[i]);
325 kprintf("\n");