6b70dd376f3f4e0752e23abdd50cb8181de2965f
[dragonfly.git] / sys / vfs / hammer / hammer_recover.c
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.17 2008/05/15 03:36:40 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 %016llx - %016llx "
87                 "(%lld bytes of UNDO)\n",
88                 root_volume->ondisk->vol_name,
89                 rootmap->first_offset, rootmap->next_offset,
90                 bytes);
91         if (bytes > (rootmap->alloc_offset & HAMMER_OFF_LONG_MASK)) {
92                 kprintf("Undo size is absurd, unable to mount\n");
93                 return(EIO);
94         }
95
96         /*
97          * Scan the UNDOs backwards.
98          */
99         scan_offset = rootmap->next_offset;
100         buffer = NULL;
101         if (scan_offset > rootmap->alloc_offset) {
102                 kprintf("HAMMER(%s) UNDO record at %016llx FIFO overflow\n",
103                         root_volume->ondisk->vol_name,
104                         scan_offset);
105                 error = EIO;
106                 goto done;
107         }
108
109         while ((int64_t)bytes > 0) {
110                 if (hammer_debug_general & 0x0080)
111                         kprintf("scan_offset %016llx\n", scan_offset);
112                 if (scan_offset == HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0)) {
113                         scan_offset = rootmap->alloc_offset;
114                         continue;
115                 }
116                 if (scan_offset - sizeof(*tail) <
117                     HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0)) {
118                         kprintf("HAMMER(%s) UNDO record at %016llx FIFO "
119                                 "underflow\n",
120                                 root_volume->ondisk->vol_name,
121                                 scan_offset);
122                         error = EIO;
123                         break;
124                 }
125                 tail = hammer_bread(hmp, scan_offset - sizeof(*tail),
126                                     &error, &buffer);
127                 if (error) {
128                         kprintf("HAMMER(%s) Unable to read UNDO TAIL "
129                                 "at %016llx\n",
130                                 root_volume->ondisk->vol_name,
131                                 scan_offset - sizeof(*tail));
132                         break;
133                 }
134
135                 if (hammer_check_tail_signature(tail, scan_offset) != 0) {
136                         kprintf("HAMMER(%s) Illegal UNDO TAIL signature "
137                                 "at %016llx\n",
138                                 root_volume->ondisk->vol_name,
139                                 scan_offset - sizeof(*tail));
140                         error = EIO;
141                         break;
142                 }
143                 undo = (void *)((char *)tail + sizeof(*tail) - tail->tail_size);
144
145                 error = hammer_recover_undo(hmp, undo,
146                                 HAMMER_BUFSIZE -
147                                 (int)((char *)undo - (char *)buffer->ondisk));
148                 if (error) {
149                         kprintf("HAMMER(%s) UNDO record at %016llx failed\n",
150                                 root_volume->ondisk->vol_name,
151                                 scan_offset - tail->tail_size);
152                         break;
153                 }
154                 scan_offset -= tail->tail_size;
155                 bytes -= tail->tail_size;
156         }
157 done:
158         /*
159          * Reload flusher_undo_start to kick off the UNDO sequencing.
160          */
161         hmp->flusher_undo_start = rootmap->next_offset;
162         if (buffer)
163                 hammer_rel_buffer(buffer, 0);
164         return (error);
165 }
166
167 static int
168 hammer_check_tail_signature(hammer_fifo_tail_t tail, hammer_off_t end_off)
169 {
170         int max_bytes;
171
172         max_bytes = ((end_off - sizeof(*tail)) & HAMMER_BUFMASK);
173         max_bytes += sizeof(*tail);
174
175         /*
176          * tail overlaps buffer boundary
177          */
178         if (((end_off - sizeof(*tail)) ^ (end_off - 1)) & ~HAMMER_BUFMASK64) {
179                 return(1);
180         }
181
182         /*
183          * signature check, the tail signature is allowed to be the head
184          * signature only for 8-byte PADs.
185          */
186         switch(tail->tail_signature) {
187         case HAMMER_TAIL_SIGNATURE:
188                 break;
189         case HAMMER_HEAD_SIGNATURE:
190                 if (tail->tail_type != HAMMER_HEAD_TYPE_PAD ||
191                     tail->tail_size != sizeof(*tail)) {
192                         return(2);
193                 }
194                 break;
195         }
196
197         /*
198          * The undo structure must not overlap a buffer boundary.
199          */
200         if (tail->tail_size < 0 || tail->tail_size > max_bytes) {
201                 return(3);
202         }
203         return(0);
204 }
205
206 static int
207 hammer_recover_undo(hammer_mount_t hmp, hammer_fifo_undo_t undo, int bytes)
208 {
209         hammer_fifo_tail_t tail;
210         hammer_volume_t volume;
211         hammer_buffer_t buffer;
212         int zone;
213         int error;
214         int vol_no;
215         int max_bytes;
216         u_int32_t offset;
217         u_int32_t crc;
218
219         /*
220          * Basic sanity checks
221          */
222         if (bytes < HAMMER_HEAD_ALIGN) {
223                 kprintf("HAMMER: Undo alignment error (%d)\n", bytes);
224                 return(EIO);
225         }
226         if (undo->head.hdr_signature != HAMMER_HEAD_SIGNATURE) {
227                 kprintf("HAMMER: Bad head signature %04x\n", 
228                         undo->head.hdr_signature);
229                 return(EIO);
230         }
231         if (undo->head.hdr_size < HAMMER_HEAD_ALIGN ||
232             undo->head.hdr_size > bytes) {
233                 kprintf("HAMMER: Bad size %d\n", bytes);
234                 return(EIO);
235         }
236
237         /*
238          * Skip PAD records.  Note that PAD records also do not require
239          * a tail and may have a truncated structure.
240          */
241         if (undo->head.hdr_type == HAMMER_HEAD_TYPE_PAD)
242                 return(0);
243
244         /*
245          * Check the CRC
246          */
247         crc = crc32(undo, HAMMER_FIFO_HEAD_CRCOFF) ^
248               crc32(&undo->head + 1, undo->head.hdr_size - sizeof(undo->head));
249         if (undo->head.hdr_crc != crc) {
250                 kprintf("HAMMER: Undo record CRC failed %08x %08x\n",
251                         undo->head.hdr_crc, crc);
252                 return(EIO);
253         }
254
255
256         /*
257          * Check the tail
258          */
259         bytes = undo->head.hdr_size;
260         tail = (void *)((char *)undo + bytes - sizeof(*tail));
261         if (tail->tail_size != undo->head.hdr_size) {
262                 kprintf("HAMMER: Bad tail size %d\n", tail->tail_size);
263                 return(EIO);
264         }
265         if (tail->tail_type != undo->head.hdr_type) {
266                 kprintf("HAMMER: Bad tail type %d\n", tail->tail_type);
267                 return(EIO);
268         }
269
270         /*
271          * Only process UNDO records
272          */
273         if (undo->head.hdr_type != HAMMER_HEAD_TYPE_UNDO)
274                 return(0);
275
276         /*
277          * Validate the UNDO record.
278          */
279         max_bytes = undo->head.hdr_size - sizeof(*undo) - sizeof(*tail);
280         if (undo->undo_data_bytes < 0 || undo->undo_data_bytes > max_bytes) {
281                 kprintf("HAMMER: Corrupt UNDO record, undo_data_bytes %d/%d\n",
282                         undo->undo_data_bytes, max_bytes);
283                 return(EIO);
284         }
285
286         /*
287          * The undo offset may only be a zone-1 or zone-2 offset.
288          *
289          * Currently we only support a zone-1 offset representing the
290          * volume header.
291          */
292         zone = HAMMER_ZONE_DECODE(undo->undo_offset);
293         offset = undo->undo_offset & HAMMER_BUFMASK;
294
295         if (offset + undo->undo_data_bytes > HAMMER_BUFSIZE) {
296                 kprintf("HAMMER: Corrupt UNDO record, bad offset\n");
297                 return (EIO);
298         }
299
300         switch(zone) {
301         case HAMMER_ZONE_RAW_VOLUME_INDEX:
302                 vol_no = HAMMER_VOL_DECODE(undo->undo_offset);
303                 volume = hammer_get_volume(hmp, vol_no, &error);
304                 if (volume == NULL) {
305                         kprintf("HAMMER: UNDO record, "
306                                 "cannot access volume %d\n", vol_no);
307                         break;
308                 }
309                 hammer_modify_volume(NULL, volume, NULL, 0);
310                 hammer_recover_copy_undo(undo->undo_offset,
311                                          (char *)(undo + 1),
312                                          (char *)volume->ondisk + offset,
313                                          undo->undo_data_bytes);
314                 hammer_modify_volume_done(volume);
315                 hammer_io_flush(&volume->io);
316                 hammer_rel_volume(volume, 0);
317                 break;
318         case HAMMER_ZONE_RAW_BUFFER_INDEX:
319                 buffer = hammer_get_buffer(hmp, undo->undo_offset, 0, &error);
320                 if (buffer == NULL) {
321                         kprintf("HAMMER: UNDO record, "
322                                 "cannot access buffer %016llx\n",
323                                 undo->undo_offset);
324                         break;
325                 }
326                 hammer_modify_buffer(NULL, buffer, NULL, 0);
327                 hammer_recover_copy_undo(undo->undo_offset,
328                                          (char *)(undo + 1),
329                                          (char *)buffer->ondisk + offset,
330                                          undo->undo_data_bytes);
331                 hammer_modify_buffer_done(buffer);
332                 hammer_io_flush(&buffer->io);
333                 hammer_rel_buffer(buffer, 0);
334                 break;
335         default:
336                 kprintf("HAMMER: Corrupt UNDO record\n");
337                 error = EIO;
338         }
339         return (error);
340 }
341
342 static void
343 hammer_recover_copy_undo(hammer_off_t undo_offset, 
344                          char *src, char *dst, int bytes)
345 {
346         hkprintf("U");
347         if (hammer_debug_general & 0x0080)
348                 kprintf("NDO %016llx: %d\n", undo_offset, bytes);
349 #if 0
350         kprintf("UNDO %016llx:", undo_offset);
351         hammer_recover_debug_dump(22, dst, bytes);
352         kprintf("%22s", "to:");
353         hammer_recover_debug_dump(22, src, bytes);
354 #endif
355         bcopy(src, dst, bytes);
356 }
357
358 #if 0
359
360 static void
361 hammer_recover_debug_dump(int w, char *buf, int bytes)
362 {
363         int i;
364
365         for (i = 0; i < bytes; ++i) {
366                 if (i && (i & 15) == 0)
367                         kprintf("\n%*.*s", w, w, "");
368                 kprintf(" %02x", (unsigned char)buf[i]);
369         }
370         kprintf("\n");
371 }
372
373 #endif