xref: /dragonfly/sys/vfs/hammer/hammer_recover.c (revision 62f7f702)
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:
10  *
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.
20  *
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.
33  *
34  * $DragonFly: src/sys/vfs/hammer/hammer_recover.c,v 1.16 2008/05/06 00:21:08 dillon Exp $
35  */
36 
37 #include "hammer.h"
38 
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 #if 0
44 static void hammer_recover_debug_dump(int w, char *buf, int bytes);
45 #endif
46 static int hammer_recover_undo(hammer_mount_t hmp, hammer_fifo_undo_t undo,
47 			int bytes);
48 
49 /*
50  * Recover a filesystem on mount
51  *
52  * NOTE: No information from the root volume has been cached in the
53  * hammer_mount structure yet, so we need to access the root volume's
54  * buffer directly.
55  */
56 int
57 hammer_recover(hammer_mount_t hmp, hammer_volume_t root_volume)
58 {
59 	hammer_blockmap_t rootmap;
60 	hammer_buffer_t buffer;
61 	hammer_off_t scan_offset;
62 	hammer_off_t bytes;
63 	hammer_fifo_tail_t tail;
64 	hammer_fifo_undo_t undo;
65 	int error;
66 
67 	/*
68 	 * Examine the UNDO FIFO.  If it is empty the filesystem is clean
69 	 * and no action need be taken.
70 	 *
71 	 * NOTE: hmp->blockmap has not been initialized yet so use the
72 	 * root volume's ondisk buffer directly.
73 	 */
74 	rootmap = &root_volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
75 	hmp->flusher_undo_start = rootmap->next_offset;
76 
77 	if (rootmap->first_offset == rootmap->next_offset)
78 		return(0);
79 
80 	if (rootmap->next_offset >= rootmap->first_offset) {
81 		bytes = rootmap->next_offset - rootmap->first_offset;
82 	} else {
83 		bytes = rootmap->alloc_offset - rootmap->first_offset +
84 			(rootmap->next_offset & HAMMER_OFF_LONG_MASK);
85 	}
86 	kprintf("HAMMER(%s) Start Recovery (%lld bytes of UNDO)\n",
87 		root_volume->ondisk->vol_name, bytes);
88 	if (bytes > (rootmap->alloc_offset & HAMMER_OFF_LONG_MASK)) {
89 		kprintf("Undo size is absurd, unable to mount\n");
90 		return(EIO);
91 	}
92 
93 	/*
94 	 * Scan the UNDOs backwards.
95 	 */
96 	scan_offset = rootmap->next_offset;
97 	buffer = NULL;
98 	if (scan_offset > rootmap->alloc_offset) {
99 		kprintf("HAMMER(%s) UNDO record at %016llx FIFO overflow\n",
100 			root_volume->ondisk->vol_name,
101 			scan_offset);
102 		error = EIO;
103 		goto done;
104 	}
105 
106 	while ((int64_t)bytes > 0) {
107 		if (hammer_debug_general & 0x0080)
108 			kprintf("scan_offset %016llx\n", scan_offset);
109 		if (scan_offset == HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0)) {
110 			scan_offset = rootmap->alloc_offset;
111 			continue;
112 		}
113 		if (scan_offset - sizeof(*tail) <
114 		    HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0)) {
115 			kprintf("HAMMER(%s) UNDO record at %016llx FIFO "
116 				"underflow\n",
117 				root_volume->ondisk->vol_name,
118 				scan_offset);
119 			error = EIO;
120 			break;
121 		}
122 		tail = hammer_bread(hmp, scan_offset - sizeof(*tail),
123 				    &error, &buffer);
124 		if (error) {
125 			kprintf("HAMMER(%s) Unable to read UNDO TAIL "
126 				"at %016llx\n",
127 				root_volume->ondisk->vol_name,
128 				scan_offset - sizeof(*tail));
129 			break;
130 		}
131 
132 		if (hammer_check_tail_signature(tail, scan_offset) != 0) {
133 			kprintf("HAMMER(%s) Illegal UNDO TAIL signature "
134 				"at %016llx\n",
135 				root_volume->ondisk->vol_name,
136 				scan_offset - sizeof(*tail));
137 			error = EIO;
138 			break;
139 		}
140 		undo = (void *)((char *)tail + sizeof(*tail) - tail->tail_size);
141 
142 		error = hammer_recover_undo(hmp, undo,
143 				HAMMER_BUFSIZE -
144 				(int)((char *)undo - (char *)buffer->ondisk));
145 		if (error) {
146 			kprintf("HAMMER(%s) UNDO record at %016llx failed\n",
147 				root_volume->ondisk->vol_name,
148 				scan_offset - tail->tail_size);
149 			break;
150 		}
151 		scan_offset -= tail->tail_size;
152 		bytes -= tail->tail_size;
153 	}
154 done:
155 	/*
156 	 * Reload flusher_undo_start to kick off the UNDO sequencing.
157 	 */
158 	hmp->flusher_undo_start = rootmap->next_offset;
159 	if (buffer)
160 		hammer_rel_buffer(buffer, 0);
161 	return (error);
162 }
163 
164 static int
165 hammer_check_tail_signature(hammer_fifo_tail_t tail, hammer_off_t end_off)
166 {
167 	int max_bytes;
168 
169 	max_bytes = ((end_off - sizeof(*tail)) & HAMMER_BUFMASK);
170 	max_bytes += sizeof(*tail);
171 
172 	/*
173 	 * tail overlaps buffer boundary
174 	 */
175 	if (((end_off - sizeof(*tail)) ^ (end_off - 1)) & ~HAMMER_BUFMASK64) {
176 		return(1);
177 	}
178 
179 	/*
180 	 * signature check, the tail signature is allowed to be the head
181 	 * signature only for 8-byte PADs.
182 	 */
183 	switch(tail->tail_signature) {
184 	case HAMMER_TAIL_SIGNATURE:
185 		break;
186 	case HAMMER_HEAD_SIGNATURE:
187 		if (tail->tail_type != HAMMER_HEAD_TYPE_PAD ||
188 		    tail->tail_size != sizeof(*tail)) {
189 			return(2);
190 		}
191 		break;
192 	}
193 
194 	/*
195 	 * The undo structure must not overlap a buffer boundary.
196 	 */
197 	if (tail->tail_size < 0 || tail->tail_size > max_bytes) {
198 		return(3);
199 	}
200 	return(0);
201 }
202 
203 static int
204 hammer_recover_undo(hammer_mount_t hmp, hammer_fifo_undo_t undo, int bytes)
205 {
206 	hammer_fifo_tail_t tail;
207 	hammer_volume_t volume;
208 	hammer_buffer_t buffer;
209 	int zone;
210 	int error;
211 	int vol_no;
212 	int max_bytes;
213 	u_int32_t offset;
214 
215 	/*
216 	 * Basic sanity checks
217 	 */
218 	if (bytes < HAMMER_HEAD_ALIGN) {
219 		kprintf("HAMMER: Undo alignment error (%d)\n", bytes);
220 		return(EIO);
221 	}
222 	if (undo->head.hdr_signature != HAMMER_HEAD_SIGNATURE) {
223 		kprintf("HAMMER: Bad head signature %04x\n",
224 			undo->head.hdr_signature);
225 		return(EIO);
226 	}
227 	if (undo->head.hdr_size < HAMMER_HEAD_ALIGN ||
228 	    undo->head.hdr_size > bytes) {
229 		kprintf("HAMMER: Bad size %d\n", bytes);
230 		return(EIO);
231 	}
232 
233 	/*
234 	 * Skip PAD records.  Note that PAD records also do not require
235 	 * a tail.
236 	 */
237 	if (undo->head.hdr_type == HAMMER_HEAD_TYPE_PAD)
238 		return(0);
239 
240 	/*
241 	 * Check the tail
242 	 */
243 	bytes = undo->head.hdr_size;
244 	tail = (void *)((char *)undo + bytes - sizeof(*tail));
245 	if (tail->tail_size != undo->head.hdr_size) {
246 		kprintf("HAMMER: Bad tail size %d\n", tail->tail_size);
247 		return(EIO);
248 	}
249 	if (tail->tail_type != undo->head.hdr_type) {
250 		kprintf("HAMMER: Bad tail type %d\n", tail->tail_type);
251 		return(EIO);
252 	}
253 
254 	/*
255 	 * Only process UNDO records
256 	 */
257 	if (undo->head.hdr_type != HAMMER_HEAD_TYPE_UNDO)
258 		return(0);
259 
260 	/*
261 	 * Validate the UNDO record.
262 	 */
263 	max_bytes = undo->head.hdr_size - sizeof(*undo) - sizeof(*tail);
264 	if (undo->undo_data_bytes < 0 || undo->undo_data_bytes > max_bytes) {
265 		kprintf("HAMMER: Corrupt UNDO record, undo_data_bytes %d/%d\n",
266 			undo->undo_data_bytes, max_bytes);
267 		return(EIO);
268 	}
269 
270 	/*
271 	 * The undo offset may only be a zone-1 or zone-2 offset.
272 	 *
273 	 * Currently we only support a zone-1 offset representing the
274 	 * volume header.
275 	 */
276 	zone = HAMMER_ZONE_DECODE(undo->undo_offset);
277 	offset = undo->undo_offset & HAMMER_BUFMASK;
278 
279 	if (offset + undo->undo_data_bytes > HAMMER_BUFSIZE) {
280 		kprintf("HAMMER: Corrupt UNDO record, bad offset\n");
281 		return (EIO);
282 	}
283 
284 	switch(zone) {
285 	case HAMMER_ZONE_RAW_VOLUME_INDEX:
286 		vol_no = HAMMER_VOL_DECODE(undo->undo_offset);
287 		volume = hammer_get_volume(hmp, vol_no, &error);
288 		if (volume == NULL) {
289 			kprintf("HAMMER: UNDO record, "
290 				"cannot access volume %d\n", vol_no);
291 			break;
292 		}
293 		hammer_modify_volume(NULL, volume, NULL, 0);
294 		hammer_recover_copy_undo(undo->undo_offset,
295 					 (char *)(undo + 1),
296 					 (char *)volume->ondisk + offset,
297 					 undo->undo_data_bytes);
298 		hammer_modify_volume_done(volume);
299 		hammer_io_flush(&volume->io);
300 		hammer_rel_volume(volume, 0);
301 		break;
302 	case HAMMER_ZONE_RAW_BUFFER_INDEX:
303 		buffer = hammer_get_buffer(hmp, undo->undo_offset, 0, &error);
304 		if (buffer == NULL) {
305 			kprintf("HAMMER: UNDO record, "
306 				"cannot access buffer %016llx\n",
307 				undo->undo_offset);
308 			break;
309 		}
310 		hammer_modify_buffer(NULL, buffer, NULL, 0);
311 		hammer_recover_copy_undo(undo->undo_offset,
312 					 (char *)(undo + 1),
313 					 (char *)buffer->ondisk + offset,
314 					 undo->undo_data_bytes);
315 		hammer_modify_buffer_done(buffer);
316 		hammer_io_flush(&buffer->io);
317 		hammer_rel_buffer(buffer, 0);
318 		break;
319 	default:
320 		kprintf("HAMMER: Corrupt UNDO record\n");
321 		error = EIO;
322 	}
323 	return (error);
324 }
325 
326 static void
327 hammer_recover_copy_undo(hammer_off_t undo_offset,
328 			 char *src, char *dst, int bytes)
329 {
330 	hkprintf("U");
331 	if (hammer_debug_general & 0x0080)
332 		kprintf("NDO %016llx: %d\n", undo_offset, bytes);
333 #if 0
334 	kprintf("UNDO %016llx:", undo_offset);
335 	hammer_recover_debug_dump(22, dst, bytes);
336 	kprintf("%22s", "to:");
337 	hammer_recover_debug_dump(22, src, bytes);
338 #endif
339 	bcopy(src, dst, bytes);
340 }
341 
342 #if 0
343 
344 static void
345 hammer_recover_debug_dump(int w, char *buf, int bytes)
346 {
347 	int i;
348 
349 	for (i = 0; i < bytes; ++i) {
350 		if (i && (i & 15) == 0)
351 			kprintf("\n%*.*s", w, w, "");
352 		kprintf(" %02x", (unsigned char)buf[i]);
353 	}
354 	kprintf("\n");
355 }
356 
357 #endif
358