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