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