1*753ac610SCharles Manning /* 2*753ac610SCharles Manning * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. 3*753ac610SCharles Manning * 4*753ac610SCharles Manning * Copyright (C) 2002-2011 Aleph One Ltd. 5*753ac610SCharles Manning * for Toby Churchill Ltd and Brightstar Engineering 6*753ac610SCharles Manning * 7*753ac610SCharles Manning * Created by Charles Manning <charles@aleph1.co.uk> 8*753ac610SCharles Manning * 9*753ac610SCharles Manning * This program is free software; you can redistribute it and/or modify 10*753ac610SCharles Manning * it under the terms of the GNU General Public License version 2 as 11*753ac610SCharles Manning * published by the Free Software Foundation. 12*753ac610SCharles Manning */ 13*753ac610SCharles Manning 14*753ac610SCharles Manning #include "yaffs_guts.h" 15*753ac610SCharles Manning #include "yaffs_trace.h" 16*753ac610SCharles Manning #include "yaffs_yaffs2.h" 17*753ac610SCharles Manning #include "yaffs_checkptrw.h" 18*753ac610SCharles Manning #include "yaffs_bitmap.h" 19*753ac610SCharles Manning #include "yaffs_nand.h" 20*753ac610SCharles Manning #include "yaffs_getblockinfo.h" 21*753ac610SCharles Manning #include "yaffs_verify.h" 22*753ac610SCharles Manning #include "yaffs_attribs.h" 23*753ac610SCharles Manning #include "yaffs_summary.h" 24*753ac610SCharles Manning 25*753ac610SCharles Manning /* 26*753ac610SCharles Manning * Checkpoints are really no benefit on very small partitions. 27*753ac610SCharles Manning * 28*753ac610SCharles Manning * To save space on small partitions don't bother with checkpoints unless 29*753ac610SCharles Manning * the partition is at least this big. 30*753ac610SCharles Manning */ 31*753ac610SCharles Manning #define YAFFS_CHECKPOINT_MIN_BLOCKS 60 32*753ac610SCharles Manning #define YAFFS_SMALL_HOLE_THRESHOLD 4 33*753ac610SCharles Manning 34*753ac610SCharles Manning /* 35*753ac610SCharles Manning * Oldest Dirty Sequence Number handling. 36*753ac610SCharles Manning */ 37*753ac610SCharles Manning 38*753ac610SCharles Manning /* yaffs_calc_oldest_dirty_seq() 39*753ac610SCharles Manning * yaffs2_find_oldest_dirty_seq() 40*753ac610SCharles Manning * Calculate the oldest dirty sequence number if we don't know it. 41*753ac610SCharles Manning */ 42*753ac610SCharles Manning void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) 43*753ac610SCharles Manning { 44*753ac610SCharles Manning int i; 45*753ac610SCharles Manning unsigned seq; 46*753ac610SCharles Manning unsigned block_no = 0; 47*753ac610SCharles Manning struct yaffs_block_info *b; 48*753ac610SCharles Manning 49*753ac610SCharles Manning if (!dev->param.is_yaffs2) 50*753ac610SCharles Manning return; 51*753ac610SCharles Manning 52*753ac610SCharles Manning /* Find the oldest dirty sequence number. */ 53*753ac610SCharles Manning seq = dev->seq_number + 1; 54*753ac610SCharles Manning b = dev->block_info; 55*753ac610SCharles Manning for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { 56*753ac610SCharles Manning if (b->block_state == YAFFS_BLOCK_STATE_FULL && 57*753ac610SCharles Manning (b->pages_in_use - b->soft_del_pages) < 58*753ac610SCharles Manning dev->param.chunks_per_block && 59*753ac610SCharles Manning b->seq_number < seq) { 60*753ac610SCharles Manning seq = b->seq_number; 61*753ac610SCharles Manning block_no = i; 62*753ac610SCharles Manning } 63*753ac610SCharles Manning b++; 64*753ac610SCharles Manning } 65*753ac610SCharles Manning 66*753ac610SCharles Manning if (block_no) { 67*753ac610SCharles Manning dev->oldest_dirty_seq = seq; 68*753ac610SCharles Manning dev->oldest_dirty_block = block_no; 69*753ac610SCharles Manning } 70*753ac610SCharles Manning } 71*753ac610SCharles Manning 72*753ac610SCharles Manning void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) 73*753ac610SCharles Manning { 74*753ac610SCharles Manning if (!dev->param.is_yaffs2) 75*753ac610SCharles Manning return; 76*753ac610SCharles Manning 77*753ac610SCharles Manning if (!dev->oldest_dirty_seq) 78*753ac610SCharles Manning yaffs_calc_oldest_dirty_seq(dev); 79*753ac610SCharles Manning } 80*753ac610SCharles Manning 81*753ac610SCharles Manning /* 82*753ac610SCharles Manning * yaffs_clear_oldest_dirty_seq() 83*753ac610SCharles Manning * Called when a block is erased or marked bad. (ie. when its seq_number 84*753ac610SCharles Manning * becomes invalid). If the value matches the oldest then we clear 85*753ac610SCharles Manning * dev->oldest_dirty_seq to force its recomputation. 86*753ac610SCharles Manning */ 87*753ac610SCharles Manning void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, 88*753ac610SCharles Manning struct yaffs_block_info *bi) 89*753ac610SCharles Manning { 90*753ac610SCharles Manning 91*753ac610SCharles Manning if (!dev->param.is_yaffs2) 92*753ac610SCharles Manning return; 93*753ac610SCharles Manning 94*753ac610SCharles Manning if (!bi || bi->seq_number == dev->oldest_dirty_seq) { 95*753ac610SCharles Manning dev->oldest_dirty_seq = 0; 96*753ac610SCharles Manning dev->oldest_dirty_block = 0; 97*753ac610SCharles Manning } 98*753ac610SCharles Manning } 99*753ac610SCharles Manning 100*753ac610SCharles Manning /* 101*753ac610SCharles Manning * yaffs2_update_oldest_dirty_seq() 102*753ac610SCharles Manning * Update the oldest dirty sequence number whenever we dirty a block. 103*753ac610SCharles Manning * Only do this if the oldest_dirty_seq is actually being tracked. 104*753ac610SCharles Manning */ 105*753ac610SCharles Manning void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, 106*753ac610SCharles Manning struct yaffs_block_info *bi) 107*753ac610SCharles Manning { 108*753ac610SCharles Manning if (!dev->param.is_yaffs2) 109*753ac610SCharles Manning return; 110*753ac610SCharles Manning 111*753ac610SCharles Manning if (dev->oldest_dirty_seq) { 112*753ac610SCharles Manning if (dev->oldest_dirty_seq > bi->seq_number) { 113*753ac610SCharles Manning dev->oldest_dirty_seq = bi->seq_number; 114*753ac610SCharles Manning dev->oldest_dirty_block = block_no; 115*753ac610SCharles Manning } 116*753ac610SCharles Manning } 117*753ac610SCharles Manning } 118*753ac610SCharles Manning 119*753ac610SCharles Manning int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) 120*753ac610SCharles Manning { 121*753ac610SCharles Manning 122*753ac610SCharles Manning if (!dev->param.is_yaffs2) 123*753ac610SCharles Manning return 1; /* disqualification only applies to yaffs2. */ 124*753ac610SCharles Manning 125*753ac610SCharles Manning if (!bi->has_shrink_hdr) 126*753ac610SCharles Manning return 1; /* can gc */ 127*753ac610SCharles Manning 128*753ac610SCharles Manning yaffs2_find_oldest_dirty_seq(dev); 129*753ac610SCharles Manning 130*753ac610SCharles Manning /* Can't do gc of this block if there are any blocks older than this 131*753ac610SCharles Manning * one that have discarded pages. 132*753ac610SCharles Manning */ 133*753ac610SCharles Manning return (bi->seq_number <= dev->oldest_dirty_seq); 134*753ac610SCharles Manning } 135*753ac610SCharles Manning 136*753ac610SCharles Manning /* 137*753ac610SCharles Manning * yaffs2_find_refresh_block() 138*753ac610SCharles Manning * periodically finds the oldest full block by sequence number for refreshing. 139*753ac610SCharles Manning * Only for yaffs2. 140*753ac610SCharles Manning */ 141*753ac610SCharles Manning u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) 142*753ac610SCharles Manning { 143*753ac610SCharles Manning u32 b; 144*753ac610SCharles Manning u32 oldest = 0; 145*753ac610SCharles Manning u32 oldest_seq = 0; 146*753ac610SCharles Manning struct yaffs_block_info *bi; 147*753ac610SCharles Manning 148*753ac610SCharles Manning if (!dev->param.is_yaffs2) 149*753ac610SCharles Manning return oldest; 150*753ac610SCharles Manning 151*753ac610SCharles Manning /* 152*753ac610SCharles Manning * If refresh period < 10 then refreshing is disabled. 153*753ac610SCharles Manning */ 154*753ac610SCharles Manning if (dev->param.refresh_period < 10) 155*753ac610SCharles Manning return oldest; 156*753ac610SCharles Manning 157*753ac610SCharles Manning /* 158*753ac610SCharles Manning * Fix broken values. 159*753ac610SCharles Manning */ 160*753ac610SCharles Manning if (dev->refresh_skip > dev->param.refresh_period) 161*753ac610SCharles Manning dev->refresh_skip = dev->param.refresh_period; 162*753ac610SCharles Manning 163*753ac610SCharles Manning if (dev->refresh_skip > 0) 164*753ac610SCharles Manning return oldest; 165*753ac610SCharles Manning 166*753ac610SCharles Manning /* 167*753ac610SCharles Manning * Refresh skip is now zero. 168*753ac610SCharles Manning * We'll do a refresh this time around.... 169*753ac610SCharles Manning * Update the refresh skip and find the oldest block. 170*753ac610SCharles Manning */ 171*753ac610SCharles Manning dev->refresh_skip = dev->param.refresh_period; 172*753ac610SCharles Manning dev->refresh_count++; 173*753ac610SCharles Manning bi = dev->block_info; 174*753ac610SCharles Manning for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { 175*753ac610SCharles Manning 176*753ac610SCharles Manning if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { 177*753ac610SCharles Manning 178*753ac610SCharles Manning if (oldest < 1 || bi->seq_number < oldest_seq) { 179*753ac610SCharles Manning oldest = b; 180*753ac610SCharles Manning oldest_seq = bi->seq_number; 181*753ac610SCharles Manning } 182*753ac610SCharles Manning } 183*753ac610SCharles Manning bi++; 184*753ac610SCharles Manning } 185*753ac610SCharles Manning 186*753ac610SCharles Manning if (oldest > 0) { 187*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_GC, 188*753ac610SCharles Manning "GC refresh count %d selected block %d with seq_number %d", 189*753ac610SCharles Manning dev->refresh_count, oldest, oldest_seq); 190*753ac610SCharles Manning } 191*753ac610SCharles Manning 192*753ac610SCharles Manning return oldest; 193*753ac610SCharles Manning } 194*753ac610SCharles Manning 195*753ac610SCharles Manning int yaffs2_checkpt_required(struct yaffs_dev *dev) 196*753ac610SCharles Manning { 197*753ac610SCharles Manning int nblocks; 198*753ac610SCharles Manning 199*753ac610SCharles Manning if (!dev->param.is_yaffs2) 200*753ac610SCharles Manning return 0; 201*753ac610SCharles Manning 202*753ac610SCharles Manning nblocks = dev->internal_end_block - dev->internal_start_block + 1; 203*753ac610SCharles Manning 204*753ac610SCharles Manning return !dev->param.skip_checkpt_wr && 205*753ac610SCharles Manning !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); 206*753ac610SCharles Manning } 207*753ac610SCharles Manning 208*753ac610SCharles Manning int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) 209*753ac610SCharles Manning { 210*753ac610SCharles Manning int retval; 211*753ac610SCharles Manning int n_bytes = 0; 212*753ac610SCharles Manning int n_blocks; 213*753ac610SCharles Manning int dev_blocks; 214*753ac610SCharles Manning 215*753ac610SCharles Manning if (!dev->param.is_yaffs2) 216*753ac610SCharles Manning return 0; 217*753ac610SCharles Manning 218*753ac610SCharles Manning if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { 219*753ac610SCharles Manning /* Not a valid value so recalculate */ 220*753ac610SCharles Manning dev_blocks = dev->param.end_block - dev->param.start_block + 1; 221*753ac610SCharles Manning n_bytes += sizeof(struct yaffs_checkpt_validity); 222*753ac610SCharles Manning n_bytes += sizeof(struct yaffs_checkpt_dev); 223*753ac610SCharles Manning n_bytes += dev_blocks * sizeof(struct yaffs_block_info); 224*753ac610SCharles Manning n_bytes += dev_blocks * dev->chunk_bit_stride; 225*753ac610SCharles Manning n_bytes += 226*753ac610SCharles Manning (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * 227*753ac610SCharles Manning dev->n_obj; 228*753ac610SCharles Manning n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; 229*753ac610SCharles Manning n_bytes += sizeof(struct yaffs_checkpt_validity); 230*753ac610SCharles Manning n_bytes += sizeof(u32); /* checksum */ 231*753ac610SCharles Manning 232*753ac610SCharles Manning /* Round up and add 2 blocks to allow for some bad blocks, 233*753ac610SCharles Manning * so add 3 */ 234*753ac610SCharles Manning 235*753ac610SCharles Manning n_blocks = 236*753ac610SCharles Manning (n_bytes / 237*753ac610SCharles Manning (dev->data_bytes_per_chunk * 238*753ac610SCharles Manning dev->param.chunks_per_block)) + 3; 239*753ac610SCharles Manning 240*753ac610SCharles Manning dev->checkpoint_blocks_required = n_blocks; 241*753ac610SCharles Manning } 242*753ac610SCharles Manning 243*753ac610SCharles Manning retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; 244*753ac610SCharles Manning if (retval < 0) 245*753ac610SCharles Manning retval = 0; 246*753ac610SCharles Manning return retval; 247*753ac610SCharles Manning } 248*753ac610SCharles Manning 249*753ac610SCharles Manning /*--------------------- Checkpointing --------------------*/ 250*753ac610SCharles Manning 251*753ac610SCharles Manning static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) 252*753ac610SCharles Manning { 253*753ac610SCharles Manning struct yaffs_checkpt_validity cp; 254*753ac610SCharles Manning 255*753ac610SCharles Manning memset(&cp, 0, sizeof(cp)); 256*753ac610SCharles Manning 257*753ac610SCharles Manning cp.struct_type = sizeof(cp); 258*753ac610SCharles Manning cp.magic = YAFFS_MAGIC; 259*753ac610SCharles Manning cp.version = YAFFS_CHECKPOINT_VERSION; 260*753ac610SCharles Manning cp.head = (head) ? 1 : 0; 261*753ac610SCharles Manning 262*753ac610SCharles Manning return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; 263*753ac610SCharles Manning } 264*753ac610SCharles Manning 265*753ac610SCharles Manning static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) 266*753ac610SCharles Manning { 267*753ac610SCharles Manning struct yaffs_checkpt_validity cp; 268*753ac610SCharles Manning int ok; 269*753ac610SCharles Manning 270*753ac610SCharles Manning ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); 271*753ac610SCharles Manning 272*753ac610SCharles Manning if (ok) 273*753ac610SCharles Manning ok = (cp.struct_type == sizeof(cp)) && 274*753ac610SCharles Manning (cp.magic == YAFFS_MAGIC) && 275*753ac610SCharles Manning (cp.version == YAFFS_CHECKPOINT_VERSION) && 276*753ac610SCharles Manning (cp.head == ((head) ? 1 : 0)); 277*753ac610SCharles Manning return ok ? 1 : 0; 278*753ac610SCharles Manning } 279*753ac610SCharles Manning 280*753ac610SCharles Manning static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, 281*753ac610SCharles Manning struct yaffs_dev *dev) 282*753ac610SCharles Manning { 283*753ac610SCharles Manning cp->n_erased_blocks = dev->n_erased_blocks; 284*753ac610SCharles Manning cp->alloc_block = dev->alloc_block; 285*753ac610SCharles Manning cp->alloc_page = dev->alloc_page; 286*753ac610SCharles Manning cp->n_free_chunks = dev->n_free_chunks; 287*753ac610SCharles Manning 288*753ac610SCharles Manning cp->n_deleted_files = dev->n_deleted_files; 289*753ac610SCharles Manning cp->n_unlinked_files = dev->n_unlinked_files; 290*753ac610SCharles Manning cp->n_bg_deletions = dev->n_bg_deletions; 291*753ac610SCharles Manning cp->seq_number = dev->seq_number; 292*753ac610SCharles Manning 293*753ac610SCharles Manning } 294*753ac610SCharles Manning 295*753ac610SCharles Manning static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, 296*753ac610SCharles Manning struct yaffs_checkpt_dev *cp) 297*753ac610SCharles Manning { 298*753ac610SCharles Manning dev->n_erased_blocks = cp->n_erased_blocks; 299*753ac610SCharles Manning dev->alloc_block = cp->alloc_block; 300*753ac610SCharles Manning dev->alloc_page = cp->alloc_page; 301*753ac610SCharles Manning dev->n_free_chunks = cp->n_free_chunks; 302*753ac610SCharles Manning 303*753ac610SCharles Manning dev->n_deleted_files = cp->n_deleted_files; 304*753ac610SCharles Manning dev->n_unlinked_files = cp->n_unlinked_files; 305*753ac610SCharles Manning dev->n_bg_deletions = cp->n_bg_deletions; 306*753ac610SCharles Manning dev->seq_number = cp->seq_number; 307*753ac610SCharles Manning } 308*753ac610SCharles Manning 309*753ac610SCharles Manning static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) 310*753ac610SCharles Manning { 311*753ac610SCharles Manning struct yaffs_checkpt_dev cp; 312*753ac610SCharles Manning u32 n_bytes; 313*753ac610SCharles Manning u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; 314*753ac610SCharles Manning int ok; 315*753ac610SCharles Manning 316*753ac610SCharles Manning /* Write device runtime values */ 317*753ac610SCharles Manning yaffs2_dev_to_checkpt_dev(&cp, dev); 318*753ac610SCharles Manning cp.struct_type = sizeof(cp); 319*753ac610SCharles Manning 320*753ac610SCharles Manning ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); 321*753ac610SCharles Manning if (!ok) 322*753ac610SCharles Manning return 0; 323*753ac610SCharles Manning 324*753ac610SCharles Manning /* Write block info */ 325*753ac610SCharles Manning n_bytes = n_blocks * sizeof(struct yaffs_block_info); 326*753ac610SCharles Manning ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); 327*753ac610SCharles Manning if (!ok) 328*753ac610SCharles Manning return 0; 329*753ac610SCharles Manning 330*753ac610SCharles Manning /* Write chunk bits */ 331*753ac610SCharles Manning n_bytes = n_blocks * dev->chunk_bit_stride; 332*753ac610SCharles Manning ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); 333*753ac610SCharles Manning 334*753ac610SCharles Manning return ok ? 1 : 0; 335*753ac610SCharles Manning } 336*753ac610SCharles Manning 337*753ac610SCharles Manning static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) 338*753ac610SCharles Manning { 339*753ac610SCharles Manning struct yaffs_checkpt_dev cp; 340*753ac610SCharles Manning u32 n_bytes; 341*753ac610SCharles Manning u32 n_blocks = 342*753ac610SCharles Manning (dev->internal_end_block - dev->internal_start_block + 1); 343*753ac610SCharles Manning int ok; 344*753ac610SCharles Manning 345*753ac610SCharles Manning ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); 346*753ac610SCharles Manning if (!ok) 347*753ac610SCharles Manning return 0; 348*753ac610SCharles Manning 349*753ac610SCharles Manning if (cp.struct_type != sizeof(cp)) 350*753ac610SCharles Manning return 0; 351*753ac610SCharles Manning 352*753ac610SCharles Manning yaffs_checkpt_dev_to_dev(dev, &cp); 353*753ac610SCharles Manning 354*753ac610SCharles Manning n_bytes = n_blocks * sizeof(struct yaffs_block_info); 355*753ac610SCharles Manning 356*753ac610SCharles Manning ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); 357*753ac610SCharles Manning 358*753ac610SCharles Manning if (!ok) 359*753ac610SCharles Manning return 0; 360*753ac610SCharles Manning 361*753ac610SCharles Manning n_bytes = n_blocks * dev->chunk_bit_stride; 362*753ac610SCharles Manning 363*753ac610SCharles Manning ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); 364*753ac610SCharles Manning 365*753ac610SCharles Manning return ok ? 1 : 0; 366*753ac610SCharles Manning } 367*753ac610SCharles Manning 368*753ac610SCharles Manning static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, 369*753ac610SCharles Manning struct yaffs_obj *obj) 370*753ac610SCharles Manning { 371*753ac610SCharles Manning cp->obj_id = obj->obj_id; 372*753ac610SCharles Manning cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; 373*753ac610SCharles Manning cp->hdr_chunk = obj->hdr_chunk; 374*753ac610SCharles Manning cp->variant_type = obj->variant_type; 375*753ac610SCharles Manning cp->deleted = obj->deleted; 376*753ac610SCharles Manning cp->soft_del = obj->soft_del; 377*753ac610SCharles Manning cp->unlinked = obj->unlinked; 378*753ac610SCharles Manning cp->fake = obj->fake; 379*753ac610SCharles Manning cp->rename_allowed = obj->rename_allowed; 380*753ac610SCharles Manning cp->unlink_allowed = obj->unlink_allowed; 381*753ac610SCharles Manning cp->serial = obj->serial; 382*753ac610SCharles Manning cp->n_data_chunks = obj->n_data_chunks; 383*753ac610SCharles Manning 384*753ac610SCharles Manning if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) 385*753ac610SCharles Manning cp->size_or_equiv_obj = obj->variant.file_variant.file_size; 386*753ac610SCharles Manning else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) 387*753ac610SCharles Manning cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; 388*753ac610SCharles Manning } 389*753ac610SCharles Manning 390*753ac610SCharles Manning static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, 391*753ac610SCharles Manning struct yaffs_checkpt_obj *cp) 392*753ac610SCharles Manning { 393*753ac610SCharles Manning struct yaffs_obj *parent; 394*753ac610SCharles Manning 395*753ac610SCharles Manning if (obj->variant_type != cp->variant_type) { 396*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_ERROR, 397*753ac610SCharles Manning "Checkpoint read object %d type %d chunk %d does not match existing object type %d", 398*753ac610SCharles Manning cp->obj_id, cp->variant_type, cp->hdr_chunk, 399*753ac610SCharles Manning obj->variant_type); 400*753ac610SCharles Manning return 0; 401*753ac610SCharles Manning } 402*753ac610SCharles Manning 403*753ac610SCharles Manning obj->obj_id = cp->obj_id; 404*753ac610SCharles Manning 405*753ac610SCharles Manning if (cp->parent_id) 406*753ac610SCharles Manning parent = yaffs_find_or_create_by_number(obj->my_dev, 407*753ac610SCharles Manning cp->parent_id, 408*753ac610SCharles Manning YAFFS_OBJECT_TYPE_DIRECTORY); 409*753ac610SCharles Manning else 410*753ac610SCharles Manning parent = NULL; 411*753ac610SCharles Manning 412*753ac610SCharles Manning if (parent) { 413*753ac610SCharles Manning if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { 414*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_ALWAYS, 415*753ac610SCharles Manning "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", 416*753ac610SCharles Manning cp->obj_id, cp->parent_id, 417*753ac610SCharles Manning cp->variant_type, cp->hdr_chunk, 418*753ac610SCharles Manning parent->variant_type); 419*753ac610SCharles Manning return 0; 420*753ac610SCharles Manning } 421*753ac610SCharles Manning yaffs_add_obj_to_dir(parent, obj); 422*753ac610SCharles Manning } 423*753ac610SCharles Manning 424*753ac610SCharles Manning obj->hdr_chunk = cp->hdr_chunk; 425*753ac610SCharles Manning obj->variant_type = cp->variant_type; 426*753ac610SCharles Manning obj->deleted = cp->deleted; 427*753ac610SCharles Manning obj->soft_del = cp->soft_del; 428*753ac610SCharles Manning obj->unlinked = cp->unlinked; 429*753ac610SCharles Manning obj->fake = cp->fake; 430*753ac610SCharles Manning obj->rename_allowed = cp->rename_allowed; 431*753ac610SCharles Manning obj->unlink_allowed = cp->unlink_allowed; 432*753ac610SCharles Manning obj->serial = cp->serial; 433*753ac610SCharles Manning obj->n_data_chunks = cp->n_data_chunks; 434*753ac610SCharles Manning 435*753ac610SCharles Manning if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) 436*753ac610SCharles Manning obj->variant.file_variant.file_size = cp->size_or_equiv_obj; 437*753ac610SCharles Manning else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) 438*753ac610SCharles Manning obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; 439*753ac610SCharles Manning 440*753ac610SCharles Manning if (obj->hdr_chunk > 0) 441*753ac610SCharles Manning obj->lazy_loaded = 1; 442*753ac610SCharles Manning return 1; 443*753ac610SCharles Manning } 444*753ac610SCharles Manning 445*753ac610SCharles Manning static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, 446*753ac610SCharles Manning struct yaffs_tnode *tn, u32 level, 447*753ac610SCharles Manning int chunk_offset) 448*753ac610SCharles Manning { 449*753ac610SCharles Manning int i; 450*753ac610SCharles Manning struct yaffs_dev *dev = in->my_dev; 451*753ac610SCharles Manning int ok = 1; 452*753ac610SCharles Manning u32 base_offset; 453*753ac610SCharles Manning 454*753ac610SCharles Manning if (!tn) 455*753ac610SCharles Manning return 1; 456*753ac610SCharles Manning 457*753ac610SCharles Manning if (level > 0) { 458*753ac610SCharles Manning for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { 459*753ac610SCharles Manning if (!tn->internal[i]) 460*753ac610SCharles Manning continue; 461*753ac610SCharles Manning ok = yaffs2_checkpt_tnode_worker(in, 462*753ac610SCharles Manning tn->internal[i], 463*753ac610SCharles Manning level - 1, 464*753ac610SCharles Manning (chunk_offset << 465*753ac610SCharles Manning YAFFS_TNODES_INTERNAL_BITS) + i); 466*753ac610SCharles Manning } 467*753ac610SCharles Manning return ok; 468*753ac610SCharles Manning } 469*753ac610SCharles Manning 470*753ac610SCharles Manning /* Level 0 tnode */ 471*753ac610SCharles Manning base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; 472*753ac610SCharles Manning ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == 473*753ac610SCharles Manning sizeof(base_offset)); 474*753ac610SCharles Manning if (ok) 475*753ac610SCharles Manning ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == 476*753ac610SCharles Manning dev->tnode_size); 477*753ac610SCharles Manning 478*753ac610SCharles Manning return ok; 479*753ac610SCharles Manning } 480*753ac610SCharles Manning 481*753ac610SCharles Manning static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) 482*753ac610SCharles Manning { 483*753ac610SCharles Manning u32 end_marker = ~0; 484*753ac610SCharles Manning int ok = 1; 485*753ac610SCharles Manning 486*753ac610SCharles Manning if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) 487*753ac610SCharles Manning return ok; 488*753ac610SCharles Manning 489*753ac610SCharles Manning ok = yaffs2_checkpt_tnode_worker(obj, 490*753ac610SCharles Manning obj->variant.file_variant.top, 491*753ac610SCharles Manning obj->variant.file_variant. 492*753ac610SCharles Manning top_level, 0); 493*753ac610SCharles Manning if (ok) 494*753ac610SCharles Manning ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, 495*753ac610SCharles Manning sizeof(end_marker)) == sizeof(end_marker)); 496*753ac610SCharles Manning 497*753ac610SCharles Manning return ok ? 1 : 0; 498*753ac610SCharles Manning } 499*753ac610SCharles Manning 500*753ac610SCharles Manning static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) 501*753ac610SCharles Manning { 502*753ac610SCharles Manning u32 base_chunk; 503*753ac610SCharles Manning int ok = 1; 504*753ac610SCharles Manning struct yaffs_dev *dev = obj->my_dev; 505*753ac610SCharles Manning struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; 506*753ac610SCharles Manning struct yaffs_tnode *tn; 507*753ac610SCharles Manning int nread = 0; 508*753ac610SCharles Manning 509*753ac610SCharles Manning ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == 510*753ac610SCharles Manning sizeof(base_chunk)); 511*753ac610SCharles Manning 512*753ac610SCharles Manning while (ok && (~base_chunk)) { 513*753ac610SCharles Manning nread++; 514*753ac610SCharles Manning /* Read level 0 tnode */ 515*753ac610SCharles Manning 516*753ac610SCharles Manning tn = yaffs_get_tnode(dev); 517*753ac610SCharles Manning if (tn) 518*753ac610SCharles Manning ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == 519*753ac610SCharles Manning dev->tnode_size); 520*753ac610SCharles Manning else 521*753ac610SCharles Manning ok = 0; 522*753ac610SCharles Manning 523*753ac610SCharles Manning if (tn && ok) 524*753ac610SCharles Manning ok = yaffs_add_find_tnode_0(dev, 525*753ac610SCharles Manning file_stuct_ptr, 526*753ac610SCharles Manning base_chunk, tn) ? 1 : 0; 527*753ac610SCharles Manning 528*753ac610SCharles Manning if (ok) 529*753ac610SCharles Manning ok = (yaffs2_checkpt_rd 530*753ac610SCharles Manning (dev, &base_chunk, 531*753ac610SCharles Manning sizeof(base_chunk)) == sizeof(base_chunk)); 532*753ac610SCharles Manning } 533*753ac610SCharles Manning 534*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 535*753ac610SCharles Manning "Checkpoint read tnodes %d records, last %d. ok %d", 536*753ac610SCharles Manning nread, base_chunk, ok); 537*753ac610SCharles Manning 538*753ac610SCharles Manning return ok ? 1 : 0; 539*753ac610SCharles Manning } 540*753ac610SCharles Manning 541*753ac610SCharles Manning static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) 542*753ac610SCharles Manning { 543*753ac610SCharles Manning struct yaffs_obj *obj; 544*753ac610SCharles Manning struct yaffs_checkpt_obj cp; 545*753ac610SCharles Manning int i; 546*753ac610SCharles Manning int ok = 1; 547*753ac610SCharles Manning struct list_head *lh; 548*753ac610SCharles Manning 549*753ac610SCharles Manning /* Iterate through the objects in each hash entry, 550*753ac610SCharles Manning * dumping them to the checkpointing stream. 551*753ac610SCharles Manning */ 552*753ac610SCharles Manning 553*753ac610SCharles Manning for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { 554*753ac610SCharles Manning list_for_each(lh, &dev->obj_bucket[i].list) { 555*753ac610SCharles Manning obj = list_entry(lh, struct yaffs_obj, hash_link); 556*753ac610SCharles Manning if (!obj->defered_free) { 557*753ac610SCharles Manning yaffs2_obj_checkpt_obj(&cp, obj); 558*753ac610SCharles Manning cp.struct_type = sizeof(cp); 559*753ac610SCharles Manning 560*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 561*753ac610SCharles Manning "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", 562*753ac610SCharles Manning cp.obj_id, cp.parent_id, 563*753ac610SCharles Manning cp.variant_type, cp.hdr_chunk, obj); 564*753ac610SCharles Manning 565*753ac610SCharles Manning ok = (yaffs2_checkpt_wr(dev, &cp, 566*753ac610SCharles Manning sizeof(cp)) == sizeof(cp)); 567*753ac610SCharles Manning 568*753ac610SCharles Manning if (ok && 569*753ac610SCharles Manning obj->variant_type == 570*753ac610SCharles Manning YAFFS_OBJECT_TYPE_FILE) 571*753ac610SCharles Manning ok = yaffs2_wr_checkpt_tnodes(obj); 572*753ac610SCharles Manning } 573*753ac610SCharles Manning } 574*753ac610SCharles Manning } 575*753ac610SCharles Manning 576*753ac610SCharles Manning /* Dump end of list */ 577*753ac610SCharles Manning memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); 578*753ac610SCharles Manning cp.struct_type = sizeof(cp); 579*753ac610SCharles Manning 580*753ac610SCharles Manning if (ok) 581*753ac610SCharles Manning ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); 582*753ac610SCharles Manning 583*753ac610SCharles Manning return ok ? 1 : 0; 584*753ac610SCharles Manning } 585*753ac610SCharles Manning 586*753ac610SCharles Manning static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) 587*753ac610SCharles Manning { 588*753ac610SCharles Manning struct yaffs_obj *obj; 589*753ac610SCharles Manning struct yaffs_checkpt_obj cp; 590*753ac610SCharles Manning int ok = 1; 591*753ac610SCharles Manning int done = 0; 592*753ac610SCharles Manning LIST_HEAD(hard_list); 593*753ac610SCharles Manning 594*753ac610SCharles Manning 595*753ac610SCharles Manning while (ok && !done) { 596*753ac610SCharles Manning ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); 597*753ac610SCharles Manning if (cp.struct_type != sizeof(cp)) { 598*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 599*753ac610SCharles Manning "struct size %d instead of %d ok %d", 600*753ac610SCharles Manning cp.struct_type, (int)sizeof(cp), ok); 601*753ac610SCharles Manning ok = 0; 602*753ac610SCharles Manning } 603*753ac610SCharles Manning 604*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 605*753ac610SCharles Manning "Checkpoint read object %d parent %d type %d chunk %d ", 606*753ac610SCharles Manning cp.obj_id, cp.parent_id, cp.variant_type, 607*753ac610SCharles Manning cp.hdr_chunk); 608*753ac610SCharles Manning 609*753ac610SCharles Manning if (ok && cp.obj_id == ~0) { 610*753ac610SCharles Manning done = 1; 611*753ac610SCharles Manning } else if (ok) { 612*753ac610SCharles Manning obj = 613*753ac610SCharles Manning yaffs_find_or_create_by_number(dev, cp.obj_id, 614*753ac610SCharles Manning cp.variant_type); 615*753ac610SCharles Manning if (obj) { 616*753ac610SCharles Manning ok = yaffs2_checkpt_obj_to_obj(obj, &cp); 617*753ac610SCharles Manning if (!ok) 618*753ac610SCharles Manning break; 619*753ac610SCharles Manning if (obj->variant_type == 620*753ac610SCharles Manning YAFFS_OBJECT_TYPE_FILE) { 621*753ac610SCharles Manning ok = yaffs2_rd_checkpt_tnodes(obj); 622*753ac610SCharles Manning } else if (obj->variant_type == 623*753ac610SCharles Manning YAFFS_OBJECT_TYPE_HARDLINK) { 624*753ac610SCharles Manning list_add(&obj->hard_links, &hard_list); 625*753ac610SCharles Manning } 626*753ac610SCharles Manning } else { 627*753ac610SCharles Manning ok = 0; 628*753ac610SCharles Manning } 629*753ac610SCharles Manning } 630*753ac610SCharles Manning } 631*753ac610SCharles Manning 632*753ac610SCharles Manning if (ok) 633*753ac610SCharles Manning yaffs_link_fixup(dev, &hard_list); 634*753ac610SCharles Manning 635*753ac610SCharles Manning return ok ? 1 : 0; 636*753ac610SCharles Manning } 637*753ac610SCharles Manning 638*753ac610SCharles Manning static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) 639*753ac610SCharles Manning { 640*753ac610SCharles Manning u32 checkpt_sum; 641*753ac610SCharles Manning int ok; 642*753ac610SCharles Manning 643*753ac610SCharles Manning yaffs2_get_checkpt_sum(dev, &checkpt_sum); 644*753ac610SCharles Manning 645*753ac610SCharles Manning ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == 646*753ac610SCharles Manning sizeof(checkpt_sum)); 647*753ac610SCharles Manning 648*753ac610SCharles Manning if (!ok) 649*753ac610SCharles Manning return 0; 650*753ac610SCharles Manning 651*753ac610SCharles Manning return 1; 652*753ac610SCharles Manning } 653*753ac610SCharles Manning 654*753ac610SCharles Manning static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) 655*753ac610SCharles Manning { 656*753ac610SCharles Manning u32 checkpt_sum0; 657*753ac610SCharles Manning u32 checkpt_sum1; 658*753ac610SCharles Manning int ok; 659*753ac610SCharles Manning 660*753ac610SCharles Manning yaffs2_get_checkpt_sum(dev, &checkpt_sum0); 661*753ac610SCharles Manning 662*753ac610SCharles Manning ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == 663*753ac610SCharles Manning sizeof(checkpt_sum1)); 664*753ac610SCharles Manning 665*753ac610SCharles Manning if (!ok) 666*753ac610SCharles Manning return 0; 667*753ac610SCharles Manning 668*753ac610SCharles Manning if (checkpt_sum0 != checkpt_sum1) 669*753ac610SCharles Manning return 0; 670*753ac610SCharles Manning 671*753ac610SCharles Manning return 1; 672*753ac610SCharles Manning } 673*753ac610SCharles Manning 674*753ac610SCharles Manning static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) 675*753ac610SCharles Manning { 676*753ac610SCharles Manning int ok = 1; 677*753ac610SCharles Manning 678*753ac610SCharles Manning if (!yaffs2_checkpt_required(dev)) { 679*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 680*753ac610SCharles Manning "skipping checkpoint write"); 681*753ac610SCharles Manning ok = 0; 682*753ac610SCharles Manning } 683*753ac610SCharles Manning 684*753ac610SCharles Manning if (ok) 685*753ac610SCharles Manning ok = yaffs2_checkpt_open(dev, 1); 686*753ac610SCharles Manning 687*753ac610SCharles Manning if (ok) { 688*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 689*753ac610SCharles Manning "write checkpoint validity"); 690*753ac610SCharles Manning ok = yaffs2_wr_checkpt_validity_marker(dev, 1); 691*753ac610SCharles Manning } 692*753ac610SCharles Manning if (ok) { 693*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 694*753ac610SCharles Manning "write checkpoint device"); 695*753ac610SCharles Manning ok = yaffs2_wr_checkpt_dev(dev); 696*753ac610SCharles Manning } 697*753ac610SCharles Manning if (ok) { 698*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 699*753ac610SCharles Manning "write checkpoint objects"); 700*753ac610SCharles Manning ok = yaffs2_wr_checkpt_objs(dev); 701*753ac610SCharles Manning } 702*753ac610SCharles Manning if (ok) { 703*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 704*753ac610SCharles Manning "write checkpoint validity"); 705*753ac610SCharles Manning ok = yaffs2_wr_checkpt_validity_marker(dev, 0); 706*753ac610SCharles Manning } 707*753ac610SCharles Manning 708*753ac610SCharles Manning if (ok) 709*753ac610SCharles Manning ok = yaffs2_wr_checkpt_sum(dev); 710*753ac610SCharles Manning 711*753ac610SCharles Manning if (!yaffs_checkpt_close(dev)) 712*753ac610SCharles Manning ok = 0; 713*753ac610SCharles Manning 714*753ac610SCharles Manning if (ok) 715*753ac610SCharles Manning dev->is_checkpointed = 1; 716*753ac610SCharles Manning else 717*753ac610SCharles Manning dev->is_checkpointed = 0; 718*753ac610SCharles Manning 719*753ac610SCharles Manning return dev->is_checkpointed; 720*753ac610SCharles Manning } 721*753ac610SCharles Manning 722*753ac610SCharles Manning static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) 723*753ac610SCharles Manning { 724*753ac610SCharles Manning int ok = 1; 725*753ac610SCharles Manning 726*753ac610SCharles Manning if (!dev->param.is_yaffs2) 727*753ac610SCharles Manning ok = 0; 728*753ac610SCharles Manning 729*753ac610SCharles Manning if (ok && dev->param.skip_checkpt_rd) { 730*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 731*753ac610SCharles Manning "skipping checkpoint read"); 732*753ac610SCharles Manning ok = 0; 733*753ac610SCharles Manning } 734*753ac610SCharles Manning 735*753ac610SCharles Manning if (ok) 736*753ac610SCharles Manning ok = yaffs2_checkpt_open(dev, 0); /* open for read */ 737*753ac610SCharles Manning 738*753ac610SCharles Manning if (ok) { 739*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 740*753ac610SCharles Manning "read checkpoint validity"); 741*753ac610SCharles Manning ok = yaffs2_rd_checkpt_validity_marker(dev, 1); 742*753ac610SCharles Manning } 743*753ac610SCharles Manning if (ok) { 744*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 745*753ac610SCharles Manning "read checkpoint device"); 746*753ac610SCharles Manning ok = yaffs2_rd_checkpt_dev(dev); 747*753ac610SCharles Manning } 748*753ac610SCharles Manning if (ok) { 749*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 750*753ac610SCharles Manning "read checkpoint objects"); 751*753ac610SCharles Manning ok = yaffs2_rd_checkpt_objs(dev); 752*753ac610SCharles Manning } 753*753ac610SCharles Manning if (ok) { 754*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 755*753ac610SCharles Manning "read checkpoint validity"); 756*753ac610SCharles Manning ok = yaffs2_rd_checkpt_validity_marker(dev, 0); 757*753ac610SCharles Manning } 758*753ac610SCharles Manning 759*753ac610SCharles Manning if (ok) { 760*753ac610SCharles Manning ok = yaffs2_rd_checkpt_sum(dev); 761*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 762*753ac610SCharles Manning "read checkpoint checksum %d", ok); 763*753ac610SCharles Manning } 764*753ac610SCharles Manning 765*753ac610SCharles Manning if (!yaffs_checkpt_close(dev)) 766*753ac610SCharles Manning ok = 0; 767*753ac610SCharles Manning 768*753ac610SCharles Manning if (ok) 769*753ac610SCharles Manning dev->is_checkpointed = 1; 770*753ac610SCharles Manning else 771*753ac610SCharles Manning dev->is_checkpointed = 0; 772*753ac610SCharles Manning 773*753ac610SCharles Manning return ok ? 1 : 0; 774*753ac610SCharles Manning } 775*753ac610SCharles Manning 776*753ac610SCharles Manning void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) 777*753ac610SCharles Manning { 778*753ac610SCharles Manning if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { 779*753ac610SCharles Manning dev->is_checkpointed = 0; 780*753ac610SCharles Manning yaffs2_checkpt_invalidate_stream(dev); 781*753ac610SCharles Manning } 782*753ac610SCharles Manning if (dev->param.sb_dirty_fn) 783*753ac610SCharles Manning dev->param.sb_dirty_fn(dev); 784*753ac610SCharles Manning } 785*753ac610SCharles Manning 786*753ac610SCharles Manning int yaffs_checkpoint_save(struct yaffs_dev *dev) 787*753ac610SCharles Manning { 788*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 789*753ac610SCharles Manning "save entry: is_checkpointed %d", 790*753ac610SCharles Manning dev->is_checkpointed); 791*753ac610SCharles Manning 792*753ac610SCharles Manning yaffs_verify_objects(dev); 793*753ac610SCharles Manning yaffs_verify_blocks(dev); 794*753ac610SCharles Manning yaffs_verify_free_chunks(dev); 795*753ac610SCharles Manning 796*753ac610SCharles Manning if (!dev->is_checkpointed) { 797*753ac610SCharles Manning yaffs2_checkpt_invalidate(dev); 798*753ac610SCharles Manning yaffs2_wr_checkpt_data(dev); 799*753ac610SCharles Manning } 800*753ac610SCharles Manning 801*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, 802*753ac610SCharles Manning "save exit: is_checkpointed %d", 803*753ac610SCharles Manning dev->is_checkpointed); 804*753ac610SCharles Manning 805*753ac610SCharles Manning return dev->is_checkpointed; 806*753ac610SCharles Manning } 807*753ac610SCharles Manning 808*753ac610SCharles Manning int yaffs2_checkpt_restore(struct yaffs_dev *dev) 809*753ac610SCharles Manning { 810*753ac610SCharles Manning int retval; 811*753ac610SCharles Manning 812*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 813*753ac610SCharles Manning "restore entry: is_checkpointed %d", 814*753ac610SCharles Manning dev->is_checkpointed); 815*753ac610SCharles Manning 816*753ac610SCharles Manning retval = yaffs2_rd_checkpt_data(dev); 817*753ac610SCharles Manning 818*753ac610SCharles Manning if (dev->is_checkpointed) { 819*753ac610SCharles Manning yaffs_verify_objects(dev); 820*753ac610SCharles Manning yaffs_verify_blocks(dev); 821*753ac610SCharles Manning yaffs_verify_free_chunks(dev); 822*753ac610SCharles Manning } 823*753ac610SCharles Manning 824*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_CHECKPOINT, 825*753ac610SCharles Manning "restore exit: is_checkpointed %d", 826*753ac610SCharles Manning dev->is_checkpointed); 827*753ac610SCharles Manning 828*753ac610SCharles Manning return retval; 829*753ac610SCharles Manning } 830*753ac610SCharles Manning 831*753ac610SCharles Manning int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) 832*753ac610SCharles Manning { 833*753ac610SCharles Manning /* if new_size > old_file_size. 834*753ac610SCharles Manning * We're going to be writing a hole. 835*753ac610SCharles Manning * If the hole is small then write zeros otherwise write a start 836*753ac610SCharles Manning * of hole marker. 837*753ac610SCharles Manning */ 838*753ac610SCharles Manning loff_t old_file_size; 839*753ac610SCharles Manning loff_t increase; 840*753ac610SCharles Manning int small_hole; 841*753ac610SCharles Manning int result = YAFFS_OK; 842*753ac610SCharles Manning struct yaffs_dev *dev = NULL; 843*753ac610SCharles Manning u8 *local_buffer = NULL; 844*753ac610SCharles Manning int small_increase_ok = 0; 845*753ac610SCharles Manning 846*753ac610SCharles Manning if (!obj) 847*753ac610SCharles Manning return YAFFS_FAIL; 848*753ac610SCharles Manning 849*753ac610SCharles Manning if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) 850*753ac610SCharles Manning return YAFFS_FAIL; 851*753ac610SCharles Manning 852*753ac610SCharles Manning dev = obj->my_dev; 853*753ac610SCharles Manning 854*753ac610SCharles Manning /* Bail out if not yaffs2 mode */ 855*753ac610SCharles Manning if (!dev->param.is_yaffs2) 856*753ac610SCharles Manning return YAFFS_OK; 857*753ac610SCharles Manning 858*753ac610SCharles Manning old_file_size = obj->variant.file_variant.file_size; 859*753ac610SCharles Manning 860*753ac610SCharles Manning if (new_size <= old_file_size) 861*753ac610SCharles Manning return YAFFS_OK; 862*753ac610SCharles Manning 863*753ac610SCharles Manning increase = new_size - old_file_size; 864*753ac610SCharles Manning 865*753ac610SCharles Manning if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && 866*753ac610SCharles Manning yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) 867*753ac610SCharles Manning small_hole = 1; 868*753ac610SCharles Manning else 869*753ac610SCharles Manning small_hole = 0; 870*753ac610SCharles Manning 871*753ac610SCharles Manning if (small_hole) 872*753ac610SCharles Manning local_buffer = yaffs_get_temp_buffer(dev); 873*753ac610SCharles Manning 874*753ac610SCharles Manning if (local_buffer) { 875*753ac610SCharles Manning /* fill hole with zero bytes */ 876*753ac610SCharles Manning loff_t pos = old_file_size; 877*753ac610SCharles Manning int this_write; 878*753ac610SCharles Manning int written; 879*753ac610SCharles Manning memset(local_buffer, 0, dev->data_bytes_per_chunk); 880*753ac610SCharles Manning small_increase_ok = 1; 881*753ac610SCharles Manning 882*753ac610SCharles Manning while (increase > 0 && small_increase_ok) { 883*753ac610SCharles Manning this_write = increase; 884*753ac610SCharles Manning if (this_write > dev->data_bytes_per_chunk) 885*753ac610SCharles Manning this_write = dev->data_bytes_per_chunk; 886*753ac610SCharles Manning written = 887*753ac610SCharles Manning yaffs_do_file_wr(obj, local_buffer, pos, this_write, 888*753ac610SCharles Manning 0); 889*753ac610SCharles Manning if (written == this_write) { 890*753ac610SCharles Manning pos += this_write; 891*753ac610SCharles Manning increase -= this_write; 892*753ac610SCharles Manning } else { 893*753ac610SCharles Manning small_increase_ok = 0; 894*753ac610SCharles Manning } 895*753ac610SCharles Manning } 896*753ac610SCharles Manning 897*753ac610SCharles Manning yaffs_release_temp_buffer(dev, local_buffer); 898*753ac610SCharles Manning 899*753ac610SCharles Manning /* If out of space then reverse any chunks we've added */ 900*753ac610SCharles Manning if (!small_increase_ok) 901*753ac610SCharles Manning yaffs_resize_file_down(obj, old_file_size); 902*753ac610SCharles Manning } 903*753ac610SCharles Manning 904*753ac610SCharles Manning if (!small_increase_ok && 905*753ac610SCharles Manning obj->parent && 906*753ac610SCharles Manning obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && 907*753ac610SCharles Manning obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { 908*753ac610SCharles Manning /* Write a hole start header with the old file size */ 909*753ac610SCharles Manning yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); 910*753ac610SCharles Manning } 911*753ac610SCharles Manning 912*753ac610SCharles Manning return result; 913*753ac610SCharles Manning } 914*753ac610SCharles Manning 915*753ac610SCharles Manning struct yaffs_block_index { 916*753ac610SCharles Manning int seq; 917*753ac610SCharles Manning int block; 918*753ac610SCharles Manning }; 919*753ac610SCharles Manning 920*753ac610SCharles Manning static int yaffs2_ybicmp(const void *a, const void *b) 921*753ac610SCharles Manning { 922*753ac610SCharles Manning int aseq = ((struct yaffs_block_index *)a)->seq; 923*753ac610SCharles Manning int bseq = ((struct yaffs_block_index *)b)->seq; 924*753ac610SCharles Manning int ablock = ((struct yaffs_block_index *)a)->block; 925*753ac610SCharles Manning int bblock = ((struct yaffs_block_index *)b)->block; 926*753ac610SCharles Manning 927*753ac610SCharles Manning if (aseq == bseq) 928*753ac610SCharles Manning return ablock - bblock; 929*753ac610SCharles Manning 930*753ac610SCharles Manning return aseq - bseq; 931*753ac610SCharles Manning } 932*753ac610SCharles Manning 933*753ac610SCharles Manning static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, 934*753ac610SCharles Manning struct yaffs_block_info *bi, 935*753ac610SCharles Manning int blk, int chunk_in_block, 936*753ac610SCharles Manning int *found_chunks, 937*753ac610SCharles Manning u8 *chunk_data, 938*753ac610SCharles Manning struct list_head *hard_list, 939*753ac610SCharles Manning int summary_available) 940*753ac610SCharles Manning { 941*753ac610SCharles Manning struct yaffs_obj_hdr *oh; 942*753ac610SCharles Manning struct yaffs_obj *in; 943*753ac610SCharles Manning struct yaffs_obj *parent; 944*753ac610SCharles Manning int equiv_id; 945*753ac610SCharles Manning loff_t file_size; 946*753ac610SCharles Manning int is_shrink; 947*753ac610SCharles Manning int is_unlinked; 948*753ac610SCharles Manning struct yaffs_ext_tags tags; 949*753ac610SCharles Manning int result; 950*753ac610SCharles Manning int alloc_failed = 0; 951*753ac610SCharles Manning int chunk = blk * dev->param.chunks_per_block + chunk_in_block; 952*753ac610SCharles Manning struct yaffs_file_var *file_var; 953*753ac610SCharles Manning struct yaffs_hardlink_var *hl_var; 954*753ac610SCharles Manning struct yaffs_symlink_var *sl_var; 955*753ac610SCharles Manning 956*753ac610SCharles Manning if (summary_available) { 957*753ac610SCharles Manning result = yaffs_summary_fetch(dev, &tags, chunk_in_block); 958*753ac610SCharles Manning tags.seq_number = bi->seq_number; 959*753ac610SCharles Manning } 960*753ac610SCharles Manning 961*753ac610SCharles Manning if (!summary_available || tags.obj_id == 0) { 962*753ac610SCharles Manning result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); 963*753ac610SCharles Manning dev->tags_used++; 964*753ac610SCharles Manning } else { 965*753ac610SCharles Manning dev->summary_used++; 966*753ac610SCharles Manning } 967*753ac610SCharles Manning 968*753ac610SCharles Manning /* Let's have a good look at this chunk... */ 969*753ac610SCharles Manning 970*753ac610SCharles Manning if (!tags.chunk_used) { 971*753ac610SCharles Manning /* An unassigned chunk in the block. 972*753ac610SCharles Manning * If there are used chunks after this one, then 973*753ac610SCharles Manning * it is a chunk that was skipped due to failing 974*753ac610SCharles Manning * the erased check. Just skip it so that it can 975*753ac610SCharles Manning * be deleted. 976*753ac610SCharles Manning * But, more typically, We get here when this is 977*753ac610SCharles Manning * an unallocated chunk and his means that 978*753ac610SCharles Manning * either the block is empty or this is the one 979*753ac610SCharles Manning * being allocated from 980*753ac610SCharles Manning */ 981*753ac610SCharles Manning 982*753ac610SCharles Manning if (*found_chunks) { 983*753ac610SCharles Manning /* This is a chunk that was skipped due 984*753ac610SCharles Manning * to failing the erased check */ 985*753ac610SCharles Manning } else if (chunk_in_block == 0) { 986*753ac610SCharles Manning /* We're looking at the first chunk in 987*753ac610SCharles Manning * the block so the block is unused */ 988*753ac610SCharles Manning bi->block_state = YAFFS_BLOCK_STATE_EMPTY; 989*753ac610SCharles Manning dev->n_erased_blocks++; 990*753ac610SCharles Manning } else { 991*753ac610SCharles Manning if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || 992*753ac610SCharles Manning bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { 993*753ac610SCharles Manning if (dev->seq_number == bi->seq_number) { 994*753ac610SCharles Manning /* Allocating from this block*/ 995*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN, 996*753ac610SCharles Manning " Allocating from %d %d", 997*753ac610SCharles Manning blk, chunk_in_block); 998*753ac610SCharles Manning 999*753ac610SCharles Manning bi->block_state = 1000*753ac610SCharles Manning YAFFS_BLOCK_STATE_ALLOCATING; 1001*753ac610SCharles Manning dev->alloc_block = blk; 1002*753ac610SCharles Manning dev->alloc_page = chunk_in_block; 1003*753ac610SCharles Manning dev->alloc_block_finder = blk; 1004*753ac610SCharles Manning } else { 1005*753ac610SCharles Manning /* This is a partially written block 1006*753ac610SCharles Manning * that is not the current 1007*753ac610SCharles Manning * allocation block. 1008*753ac610SCharles Manning */ 1009*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN, 1010*753ac610SCharles Manning "Partially written block %d detected. gc will fix this.", 1011*753ac610SCharles Manning blk); 1012*753ac610SCharles Manning } 1013*753ac610SCharles Manning } 1014*753ac610SCharles Manning } 1015*753ac610SCharles Manning 1016*753ac610SCharles Manning dev->n_free_chunks++; 1017*753ac610SCharles Manning 1018*753ac610SCharles Manning } else if (tags.ecc_result == 1019*753ac610SCharles Manning YAFFS_ECC_RESULT_UNFIXED) { 1020*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN, 1021*753ac610SCharles Manning " Unfixed ECC in chunk(%d:%d), chunk ignored", 1022*753ac610SCharles Manning blk, chunk_in_block); 1023*753ac610SCharles Manning dev->n_free_chunks++; 1024*753ac610SCharles Manning } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || 1025*753ac610SCharles Manning tags.chunk_id > YAFFS_MAX_CHUNK_ID || 1026*753ac610SCharles Manning tags.obj_id == YAFFS_OBJECTID_SUMMARY || 1027*753ac610SCharles Manning (tags.chunk_id > 0 && 1028*753ac610SCharles Manning tags.n_bytes > dev->data_bytes_per_chunk) || 1029*753ac610SCharles Manning tags.seq_number != bi->seq_number) { 1030*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN, 1031*753ac610SCharles Manning "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", 1032*753ac610SCharles Manning blk, chunk_in_block, tags.obj_id, 1033*753ac610SCharles Manning tags.chunk_id, tags.n_bytes); 1034*753ac610SCharles Manning dev->n_free_chunks++; 1035*753ac610SCharles Manning } else if (tags.chunk_id > 0) { 1036*753ac610SCharles Manning /* chunk_id > 0 so it is a data chunk... */ 1037*753ac610SCharles Manning loff_t endpos; 1038*753ac610SCharles Manning loff_t chunk_base = (tags.chunk_id - 1) * 1039*753ac610SCharles Manning dev->data_bytes_per_chunk; 1040*753ac610SCharles Manning 1041*753ac610SCharles Manning *found_chunks = 1; 1042*753ac610SCharles Manning 1043*753ac610SCharles Manning yaffs_set_chunk_bit(dev, blk, chunk_in_block); 1044*753ac610SCharles Manning bi->pages_in_use++; 1045*753ac610SCharles Manning 1046*753ac610SCharles Manning in = yaffs_find_or_create_by_number(dev, 1047*753ac610SCharles Manning tags.obj_id, 1048*753ac610SCharles Manning YAFFS_OBJECT_TYPE_FILE); 1049*753ac610SCharles Manning if (!in) 1050*753ac610SCharles Manning /* Out of memory */ 1051*753ac610SCharles Manning alloc_failed = 1; 1052*753ac610SCharles Manning 1053*753ac610SCharles Manning if (in && 1054*753ac610SCharles Manning in->variant_type == YAFFS_OBJECT_TYPE_FILE && 1055*753ac610SCharles Manning chunk_base < in->variant.file_variant.shrink_size) { 1056*753ac610SCharles Manning /* This has not been invalidated by 1057*753ac610SCharles Manning * a resize */ 1058*753ac610SCharles Manning if (!yaffs_put_chunk_in_file(in, tags.chunk_id, 1059*753ac610SCharles Manning chunk, -1)) 1060*753ac610SCharles Manning alloc_failed = 1; 1061*753ac610SCharles Manning 1062*753ac610SCharles Manning /* File size is calculated by looking at 1063*753ac610SCharles Manning * the data chunks if we have not 1064*753ac610SCharles Manning * seen an object header yet. 1065*753ac610SCharles Manning * Stop this practice once we find an 1066*753ac610SCharles Manning * object header. 1067*753ac610SCharles Manning */ 1068*753ac610SCharles Manning endpos = chunk_base + tags.n_bytes; 1069*753ac610SCharles Manning 1070*753ac610SCharles Manning if (!in->valid && 1071*753ac610SCharles Manning in->variant.file_variant.scanned_size < endpos) { 1072*753ac610SCharles Manning in->variant.file_variant. 1073*753ac610SCharles Manning scanned_size = endpos; 1074*753ac610SCharles Manning in->variant.file_variant. 1075*753ac610SCharles Manning file_size = endpos; 1076*753ac610SCharles Manning } 1077*753ac610SCharles Manning } else if (in) { 1078*753ac610SCharles Manning /* This chunk has been invalidated by a 1079*753ac610SCharles Manning * resize, or a past file deletion 1080*753ac610SCharles Manning * so delete the chunk*/ 1081*753ac610SCharles Manning yaffs_chunk_del(dev, chunk, 1, __LINE__); 1082*753ac610SCharles Manning } 1083*753ac610SCharles Manning } else { 1084*753ac610SCharles Manning /* chunk_id == 0, so it is an ObjectHeader. 1085*753ac610SCharles Manning * Thus, we read in the object header and make 1086*753ac610SCharles Manning * the object 1087*753ac610SCharles Manning */ 1088*753ac610SCharles Manning *found_chunks = 1; 1089*753ac610SCharles Manning 1090*753ac610SCharles Manning yaffs_set_chunk_bit(dev, blk, chunk_in_block); 1091*753ac610SCharles Manning bi->pages_in_use++; 1092*753ac610SCharles Manning 1093*753ac610SCharles Manning oh = NULL; 1094*753ac610SCharles Manning in = NULL; 1095*753ac610SCharles Manning 1096*753ac610SCharles Manning if (tags.extra_available) { 1097*753ac610SCharles Manning in = yaffs_find_or_create_by_number(dev, 1098*753ac610SCharles Manning tags.obj_id, 1099*753ac610SCharles Manning tags.extra_obj_type); 1100*753ac610SCharles Manning if (!in) 1101*753ac610SCharles Manning alloc_failed = 1; 1102*753ac610SCharles Manning } 1103*753ac610SCharles Manning 1104*753ac610SCharles Manning if (!in || 1105*753ac610SCharles Manning (!in->valid && dev->param.disable_lazy_load) || 1106*753ac610SCharles Manning tags.extra_shadows || 1107*753ac610SCharles Manning (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || 1108*753ac610SCharles Manning tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { 1109*753ac610SCharles Manning 1110*753ac610SCharles Manning /* If we don't have valid info then we 1111*753ac610SCharles Manning * need to read the chunk 1112*753ac610SCharles Manning * TODO In future we can probably defer 1113*753ac610SCharles Manning * reading the chunk and living with 1114*753ac610SCharles Manning * invalid data until needed. 1115*753ac610SCharles Manning */ 1116*753ac610SCharles Manning 1117*753ac610SCharles Manning result = yaffs_rd_chunk_tags_nand(dev, 1118*753ac610SCharles Manning chunk, 1119*753ac610SCharles Manning chunk_data, 1120*753ac610SCharles Manning NULL); 1121*753ac610SCharles Manning 1122*753ac610SCharles Manning oh = (struct yaffs_obj_hdr *)chunk_data; 1123*753ac610SCharles Manning 1124*753ac610SCharles Manning if (dev->param.inband_tags) { 1125*753ac610SCharles Manning /* Fix up the header if they got 1126*753ac610SCharles Manning * corrupted by inband tags */ 1127*753ac610SCharles Manning oh->shadows_obj = 1128*753ac610SCharles Manning oh->inband_shadowed_obj_id; 1129*753ac610SCharles Manning oh->is_shrink = 1130*753ac610SCharles Manning oh->inband_is_shrink; 1131*753ac610SCharles Manning } 1132*753ac610SCharles Manning 1133*753ac610SCharles Manning if (!in) { 1134*753ac610SCharles Manning in = yaffs_find_or_create_by_number(dev, 1135*753ac610SCharles Manning tags.obj_id, oh->type); 1136*753ac610SCharles Manning if (!in) 1137*753ac610SCharles Manning alloc_failed = 1; 1138*753ac610SCharles Manning } 1139*753ac610SCharles Manning } 1140*753ac610SCharles Manning 1141*753ac610SCharles Manning if (!in) { 1142*753ac610SCharles Manning /* TODO Hoosterman we have a problem! */ 1143*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_ERROR, 1144*753ac610SCharles Manning "yaffs tragedy: Could not make object for object %d at chunk %d during scan", 1145*753ac610SCharles Manning tags.obj_id, chunk); 1146*753ac610SCharles Manning return YAFFS_FAIL; 1147*753ac610SCharles Manning } 1148*753ac610SCharles Manning 1149*753ac610SCharles Manning if (in->valid) { 1150*753ac610SCharles Manning /* We have already filled this one. 1151*753ac610SCharles Manning * We have a duplicate that will be 1152*753ac610SCharles Manning * discarded, but we first have to suck 1153*753ac610SCharles Manning * out resize info if it is a file. 1154*753ac610SCharles Manning */ 1155*753ac610SCharles Manning if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && 1156*753ac610SCharles Manning ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || 1157*753ac610SCharles Manning (tags.extra_available && 1158*753ac610SCharles Manning tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) 1159*753ac610SCharles Manning )) { 1160*753ac610SCharles Manning loff_t this_size = (oh) ? 1161*753ac610SCharles Manning yaffs_oh_to_size(oh) : 1162*753ac610SCharles Manning tags.extra_file_size; 1163*753ac610SCharles Manning u32 parent_obj_id = (oh) ? 1164*753ac610SCharles Manning oh->parent_obj_id : 1165*753ac610SCharles Manning tags.extra_parent_id; 1166*753ac610SCharles Manning 1167*753ac610SCharles Manning is_shrink = (oh) ? 1168*753ac610SCharles Manning oh->is_shrink : 1169*753ac610SCharles Manning tags.extra_is_shrink; 1170*753ac610SCharles Manning 1171*753ac610SCharles Manning /* If it is deleted (unlinked 1172*753ac610SCharles Manning * at start also means deleted) 1173*753ac610SCharles Manning * we treat the file size as 1174*753ac610SCharles Manning * being zeroed at this point. 1175*753ac610SCharles Manning */ 1176*753ac610SCharles Manning if (parent_obj_id == YAFFS_OBJECTID_DELETED || 1177*753ac610SCharles Manning parent_obj_id == YAFFS_OBJECTID_UNLINKED) { 1178*753ac610SCharles Manning this_size = 0; 1179*753ac610SCharles Manning is_shrink = 1; 1180*753ac610SCharles Manning } 1181*753ac610SCharles Manning 1182*753ac610SCharles Manning if (is_shrink && 1183*753ac610SCharles Manning in->variant.file_variant.shrink_size > 1184*753ac610SCharles Manning this_size) 1185*753ac610SCharles Manning in->variant.file_variant.shrink_size = 1186*753ac610SCharles Manning this_size; 1187*753ac610SCharles Manning 1188*753ac610SCharles Manning if (is_shrink) 1189*753ac610SCharles Manning bi->has_shrink_hdr = 1; 1190*753ac610SCharles Manning } 1191*753ac610SCharles Manning /* Use existing - destroy this one. */ 1192*753ac610SCharles Manning yaffs_chunk_del(dev, chunk, 1, __LINE__); 1193*753ac610SCharles Manning } 1194*753ac610SCharles Manning 1195*753ac610SCharles Manning if (!in->valid && in->variant_type != 1196*753ac610SCharles Manning (oh ? oh->type : tags.extra_obj_type)) 1197*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_ERROR, 1198*753ac610SCharles Manning "yaffs tragedy: Bad object type, %d != %d, for object %d at chunk %d during scan", 1199*753ac610SCharles Manning oh ? oh->type : tags.extra_obj_type, 1200*753ac610SCharles Manning in->variant_type, tags.obj_id, 1201*753ac610SCharles Manning chunk); 1202*753ac610SCharles Manning 1203*753ac610SCharles Manning if (!in->valid && 1204*753ac610SCharles Manning (tags.obj_id == YAFFS_OBJECTID_ROOT || 1205*753ac610SCharles Manning tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { 1206*753ac610SCharles Manning /* We only load some info, don't fiddle 1207*753ac610SCharles Manning * with directory structure */ 1208*753ac610SCharles Manning in->valid = 1; 1209*753ac610SCharles Manning 1210*753ac610SCharles Manning if (oh) { 1211*753ac610SCharles Manning in->yst_mode = oh->yst_mode; 1212*753ac610SCharles Manning yaffs_load_attribs(in, oh); 1213*753ac610SCharles Manning in->lazy_loaded = 0; 1214*753ac610SCharles Manning } else { 1215*753ac610SCharles Manning in->lazy_loaded = 1; 1216*753ac610SCharles Manning } 1217*753ac610SCharles Manning in->hdr_chunk = chunk; 1218*753ac610SCharles Manning 1219*753ac610SCharles Manning } else if (!in->valid) { 1220*753ac610SCharles Manning /* we need to load this info */ 1221*753ac610SCharles Manning in->valid = 1; 1222*753ac610SCharles Manning in->hdr_chunk = chunk; 1223*753ac610SCharles Manning if (oh) { 1224*753ac610SCharles Manning in->variant_type = oh->type; 1225*753ac610SCharles Manning in->yst_mode = oh->yst_mode; 1226*753ac610SCharles Manning yaffs_load_attribs(in, oh); 1227*753ac610SCharles Manning 1228*753ac610SCharles Manning if (oh->shadows_obj > 0) 1229*753ac610SCharles Manning yaffs_handle_shadowed_obj(dev, 1230*753ac610SCharles Manning oh->shadows_obj, 1); 1231*753ac610SCharles Manning 1232*753ac610SCharles Manning yaffs_set_obj_name_from_oh(in, oh); 1233*753ac610SCharles Manning parent = yaffs_find_or_create_by_number(dev, 1234*753ac610SCharles Manning oh->parent_obj_id, 1235*753ac610SCharles Manning YAFFS_OBJECT_TYPE_DIRECTORY); 1236*753ac610SCharles Manning file_size = yaffs_oh_to_size(oh); 1237*753ac610SCharles Manning is_shrink = oh->is_shrink; 1238*753ac610SCharles Manning equiv_id = oh->equiv_id; 1239*753ac610SCharles Manning } else { 1240*753ac610SCharles Manning in->variant_type = tags.extra_obj_type; 1241*753ac610SCharles Manning parent = yaffs_find_or_create_by_number(dev, 1242*753ac610SCharles Manning tags.extra_parent_id, 1243*753ac610SCharles Manning YAFFS_OBJECT_TYPE_DIRECTORY); 1244*753ac610SCharles Manning file_size = tags.extra_file_size; 1245*753ac610SCharles Manning is_shrink = tags.extra_is_shrink; 1246*753ac610SCharles Manning equiv_id = tags.extra_equiv_id; 1247*753ac610SCharles Manning in->lazy_loaded = 1; 1248*753ac610SCharles Manning } 1249*753ac610SCharles Manning in->dirty = 0; 1250*753ac610SCharles Manning 1251*753ac610SCharles Manning if (!parent) 1252*753ac610SCharles Manning alloc_failed = 1; 1253*753ac610SCharles Manning 1254*753ac610SCharles Manning /* directory stuff... 1255*753ac610SCharles Manning * hook up to parent 1256*753ac610SCharles Manning */ 1257*753ac610SCharles Manning 1258*753ac610SCharles Manning if (parent && 1259*753ac610SCharles Manning parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { 1260*753ac610SCharles Manning /* Set up as a directory */ 1261*753ac610SCharles Manning parent->variant_type = 1262*753ac610SCharles Manning YAFFS_OBJECT_TYPE_DIRECTORY; 1263*753ac610SCharles Manning INIT_LIST_HEAD(&parent-> 1264*753ac610SCharles Manning variant.dir_variant.children); 1265*753ac610SCharles Manning } else if (!parent || 1266*753ac610SCharles Manning parent->variant_type != 1267*753ac610SCharles Manning YAFFS_OBJECT_TYPE_DIRECTORY) { 1268*753ac610SCharles Manning /* Hoosterman, another problem.... 1269*753ac610SCharles Manning * Trying to use a non-directory as a directory 1270*753ac610SCharles Manning */ 1271*753ac610SCharles Manning 1272*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_ERROR, 1273*753ac610SCharles Manning "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." 1274*753ac610SCharles Manning ); 1275*753ac610SCharles Manning parent = dev->lost_n_found; 1276*753ac610SCharles Manning } 1277*753ac610SCharles Manning yaffs_add_obj_to_dir(parent, in); 1278*753ac610SCharles Manning 1279*753ac610SCharles Manning is_unlinked = (parent == dev->del_dir) || 1280*753ac610SCharles Manning (parent == dev->unlinked_dir); 1281*753ac610SCharles Manning 1282*753ac610SCharles Manning if (is_shrink) 1283*753ac610SCharles Manning /* Mark the block */ 1284*753ac610SCharles Manning bi->has_shrink_hdr = 1; 1285*753ac610SCharles Manning 1286*753ac610SCharles Manning /* Note re hardlinks. 1287*753ac610SCharles Manning * Since we might scan a hardlink before its equivalent 1288*753ac610SCharles Manning * object is scanned we put them all in a list. 1289*753ac610SCharles Manning * After scanning is complete, we should have all the 1290*753ac610SCharles Manning * objects, so we run through this list and fix up all 1291*753ac610SCharles Manning * the chains. 1292*753ac610SCharles Manning */ 1293*753ac610SCharles Manning 1294*753ac610SCharles Manning switch (in->variant_type) { 1295*753ac610SCharles Manning case YAFFS_OBJECT_TYPE_UNKNOWN: 1296*753ac610SCharles Manning /* Todo got a problem */ 1297*753ac610SCharles Manning break; 1298*753ac610SCharles Manning case YAFFS_OBJECT_TYPE_FILE: 1299*753ac610SCharles Manning file_var = &in->variant.file_variant; 1300*753ac610SCharles Manning if (file_var->scanned_size < file_size) { 1301*753ac610SCharles Manning /* This covers the case where the file 1302*753ac610SCharles Manning * size is greater than the data held. 1303*753ac610SCharles Manning * This will happen if the file is 1304*753ac610SCharles Manning * resized to be larger than its 1305*753ac610SCharles Manning * current data extents. 1306*753ac610SCharles Manning */ 1307*753ac610SCharles Manning file_var->file_size = file_size; 1308*753ac610SCharles Manning file_var->scanned_size = file_size; 1309*753ac610SCharles Manning } 1310*753ac610SCharles Manning 1311*753ac610SCharles Manning if (file_var->shrink_size > file_size) 1312*753ac610SCharles Manning file_var->shrink_size = file_size; 1313*753ac610SCharles Manning 1314*753ac610SCharles Manning break; 1315*753ac610SCharles Manning case YAFFS_OBJECT_TYPE_HARDLINK: 1316*753ac610SCharles Manning hl_var = &in->variant.hardlink_variant; 1317*753ac610SCharles Manning if (!is_unlinked) { 1318*753ac610SCharles Manning hl_var->equiv_id = equiv_id; 1319*753ac610SCharles Manning list_add(&in->hard_links, hard_list); 1320*753ac610SCharles Manning } 1321*753ac610SCharles Manning break; 1322*753ac610SCharles Manning case YAFFS_OBJECT_TYPE_DIRECTORY: 1323*753ac610SCharles Manning /* Do nothing */ 1324*753ac610SCharles Manning break; 1325*753ac610SCharles Manning case YAFFS_OBJECT_TYPE_SPECIAL: 1326*753ac610SCharles Manning /* Do nothing */ 1327*753ac610SCharles Manning break; 1328*753ac610SCharles Manning case YAFFS_OBJECT_TYPE_SYMLINK: 1329*753ac610SCharles Manning sl_var = &in->variant.symlink_variant; 1330*753ac610SCharles Manning if (oh) { 1331*753ac610SCharles Manning sl_var->alias = 1332*753ac610SCharles Manning yaffs_clone_str(oh->alias); 1333*753ac610SCharles Manning if (!sl_var->alias) 1334*753ac610SCharles Manning alloc_failed = 1; 1335*753ac610SCharles Manning } 1336*753ac610SCharles Manning break; 1337*753ac610SCharles Manning } 1338*753ac610SCharles Manning } 1339*753ac610SCharles Manning } 1340*753ac610SCharles Manning return alloc_failed ? YAFFS_FAIL : YAFFS_OK; 1341*753ac610SCharles Manning } 1342*753ac610SCharles Manning 1343*753ac610SCharles Manning int yaffs2_scan_backwards(struct yaffs_dev *dev) 1344*753ac610SCharles Manning { 1345*753ac610SCharles Manning int blk; 1346*753ac610SCharles Manning int block_iter; 1347*753ac610SCharles Manning int start_iter; 1348*753ac610SCharles Manning int end_iter; 1349*753ac610SCharles Manning int n_to_scan = 0; 1350*753ac610SCharles Manning enum yaffs_block_state state; 1351*753ac610SCharles Manning int c; 1352*753ac610SCharles Manning int deleted; 1353*753ac610SCharles Manning LIST_HEAD(hard_list); 1354*753ac610SCharles Manning struct yaffs_block_info *bi; 1355*753ac610SCharles Manning u32 seq_number; 1356*753ac610SCharles Manning int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; 1357*753ac610SCharles Manning u8 *chunk_data; 1358*753ac610SCharles Manning int found_chunks; 1359*753ac610SCharles Manning int alloc_failed = 0; 1360*753ac610SCharles Manning struct yaffs_block_index *block_index = NULL; 1361*753ac610SCharles Manning int alt_block_index = 0; 1362*753ac610SCharles Manning int summary_available; 1363*753ac610SCharles Manning 1364*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN, 1365*753ac610SCharles Manning "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", 1366*753ac610SCharles Manning dev->internal_start_block, dev->internal_end_block); 1367*753ac610SCharles Manning 1368*753ac610SCharles Manning dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; 1369*753ac610SCharles Manning 1370*753ac610SCharles Manning block_index = 1371*753ac610SCharles Manning kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); 1372*753ac610SCharles Manning 1373*753ac610SCharles Manning if (!block_index) { 1374*753ac610SCharles Manning block_index = 1375*753ac610SCharles Manning vmalloc(n_blocks * sizeof(struct yaffs_block_index)); 1376*753ac610SCharles Manning alt_block_index = 1; 1377*753ac610SCharles Manning } 1378*753ac610SCharles Manning 1379*753ac610SCharles Manning if (!block_index) { 1380*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN, 1381*753ac610SCharles Manning "yaffs2_scan_backwards() could not allocate block index!" 1382*753ac610SCharles Manning ); 1383*753ac610SCharles Manning return YAFFS_FAIL; 1384*753ac610SCharles Manning } 1385*753ac610SCharles Manning 1386*753ac610SCharles Manning dev->blocks_in_checkpt = 0; 1387*753ac610SCharles Manning 1388*753ac610SCharles Manning chunk_data = yaffs_get_temp_buffer(dev); 1389*753ac610SCharles Manning 1390*753ac610SCharles Manning /* Scan all the blocks to determine their state */ 1391*753ac610SCharles Manning bi = dev->block_info; 1392*753ac610SCharles Manning for (blk = dev->internal_start_block; blk <= dev->internal_end_block; 1393*753ac610SCharles Manning blk++) { 1394*753ac610SCharles Manning yaffs_clear_chunk_bits(dev, blk); 1395*753ac610SCharles Manning bi->pages_in_use = 0; 1396*753ac610SCharles Manning bi->soft_del_pages = 0; 1397*753ac610SCharles Manning 1398*753ac610SCharles Manning yaffs_query_init_block_state(dev, blk, &state, &seq_number); 1399*753ac610SCharles Manning 1400*753ac610SCharles Manning bi->block_state = state; 1401*753ac610SCharles Manning bi->seq_number = seq_number; 1402*753ac610SCharles Manning 1403*753ac610SCharles Manning if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) 1404*753ac610SCharles Manning bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; 1405*753ac610SCharles Manning if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) 1406*753ac610SCharles Manning bi->block_state = YAFFS_BLOCK_STATE_DEAD; 1407*753ac610SCharles Manning 1408*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, 1409*753ac610SCharles Manning "Block scanning block %d state %d seq %d", 1410*753ac610SCharles Manning blk, bi->block_state, seq_number); 1411*753ac610SCharles Manning 1412*753ac610SCharles Manning if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { 1413*753ac610SCharles Manning dev->blocks_in_checkpt++; 1414*753ac610SCharles Manning 1415*753ac610SCharles Manning } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { 1416*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, 1417*753ac610SCharles Manning "block %d is bad", blk); 1418*753ac610SCharles Manning } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { 1419*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); 1420*753ac610SCharles Manning dev->n_erased_blocks++; 1421*753ac610SCharles Manning dev->n_free_chunks += dev->param.chunks_per_block; 1422*753ac610SCharles Manning } else if (bi->block_state == 1423*753ac610SCharles Manning YAFFS_BLOCK_STATE_NEEDS_SCAN) { 1424*753ac610SCharles Manning /* Determine the highest sequence number */ 1425*753ac610SCharles Manning if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && 1426*753ac610SCharles Manning seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { 1427*753ac610SCharles Manning block_index[n_to_scan].seq = seq_number; 1428*753ac610SCharles Manning block_index[n_to_scan].block = blk; 1429*753ac610SCharles Manning n_to_scan++; 1430*753ac610SCharles Manning if (seq_number >= dev->seq_number) 1431*753ac610SCharles Manning dev->seq_number = seq_number; 1432*753ac610SCharles Manning } else { 1433*753ac610SCharles Manning /* TODO: Nasty sequence number! */ 1434*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN, 1435*753ac610SCharles Manning "Block scanning block %d has bad sequence number %d", 1436*753ac610SCharles Manning blk, seq_number); 1437*753ac610SCharles Manning } 1438*753ac610SCharles Manning } 1439*753ac610SCharles Manning bi++; 1440*753ac610SCharles Manning } 1441*753ac610SCharles Manning 1442*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN, "%d blocks to be sorted...", n_to_scan); 1443*753ac610SCharles Manning 1444*753ac610SCharles Manning cond_resched(); 1445*753ac610SCharles Manning 1446*753ac610SCharles Manning /* Sort the blocks by sequence number */ 1447*753ac610SCharles Manning sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), 1448*753ac610SCharles Manning yaffs2_ybicmp, NULL); 1449*753ac610SCharles Manning 1450*753ac610SCharles Manning cond_resched(); 1451*753ac610SCharles Manning 1452*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN, "...done"); 1453*753ac610SCharles Manning 1454*753ac610SCharles Manning /* Now scan the blocks looking at the data. */ 1455*753ac610SCharles Manning start_iter = 0; 1456*753ac610SCharles Manning end_iter = n_to_scan - 1; 1457*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); 1458*753ac610SCharles Manning 1459*753ac610SCharles Manning /* For each block.... backwards */ 1460*753ac610SCharles Manning for (block_iter = end_iter; 1461*753ac610SCharles Manning !alloc_failed && block_iter >= start_iter; 1462*753ac610SCharles Manning block_iter--) { 1463*753ac610SCharles Manning /* Cooperative multitasking! This loop can run for so 1464*753ac610SCharles Manning long that watchdog timers expire. */ 1465*753ac610SCharles Manning cond_resched(); 1466*753ac610SCharles Manning 1467*753ac610SCharles Manning /* get the block to scan in the correct order */ 1468*753ac610SCharles Manning blk = block_index[block_iter].block; 1469*753ac610SCharles Manning bi = yaffs_get_block_info(dev, blk); 1470*753ac610SCharles Manning deleted = 0; 1471*753ac610SCharles Manning 1472*753ac610SCharles Manning summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); 1473*753ac610SCharles Manning 1474*753ac610SCharles Manning /* For each chunk in each block that needs scanning.... */ 1475*753ac610SCharles Manning found_chunks = 0; 1476*753ac610SCharles Manning if (summary_available) 1477*753ac610SCharles Manning c = dev->chunks_per_summary - 1; 1478*753ac610SCharles Manning else 1479*753ac610SCharles Manning c = dev->param.chunks_per_block - 1; 1480*753ac610SCharles Manning 1481*753ac610SCharles Manning for (/* c is already initialised */; 1482*753ac610SCharles Manning !alloc_failed && c >= 0 && 1483*753ac610SCharles Manning (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || 1484*753ac610SCharles Manning bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); 1485*753ac610SCharles Manning c--) { 1486*753ac610SCharles Manning /* Scan backwards... 1487*753ac610SCharles Manning * Read the tags and decide what to do 1488*753ac610SCharles Manning */ 1489*753ac610SCharles Manning if (yaffs2_scan_chunk(dev, bi, blk, c, 1490*753ac610SCharles Manning &found_chunks, chunk_data, 1491*753ac610SCharles Manning &hard_list, summary_available) == 1492*753ac610SCharles Manning YAFFS_FAIL) 1493*753ac610SCharles Manning alloc_failed = 1; 1494*753ac610SCharles Manning } 1495*753ac610SCharles Manning 1496*753ac610SCharles Manning if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { 1497*753ac610SCharles Manning /* If we got this far while scanning, then the block 1498*753ac610SCharles Manning * is fully allocated. */ 1499*753ac610SCharles Manning bi->block_state = YAFFS_BLOCK_STATE_FULL; 1500*753ac610SCharles Manning } 1501*753ac610SCharles Manning 1502*753ac610SCharles Manning /* Now let's see if it was dirty */ 1503*753ac610SCharles Manning if (bi->pages_in_use == 0 && 1504*753ac610SCharles Manning !bi->has_shrink_hdr && 1505*753ac610SCharles Manning bi->block_state == YAFFS_BLOCK_STATE_FULL) { 1506*753ac610SCharles Manning yaffs_block_became_dirty(dev, blk); 1507*753ac610SCharles Manning } 1508*753ac610SCharles Manning } 1509*753ac610SCharles Manning 1510*753ac610SCharles Manning yaffs_skip_rest_of_block(dev); 1511*753ac610SCharles Manning 1512*753ac610SCharles Manning if (alt_block_index) 1513*753ac610SCharles Manning vfree(block_index); 1514*753ac610SCharles Manning else 1515*753ac610SCharles Manning kfree(block_index); 1516*753ac610SCharles Manning 1517*753ac610SCharles Manning /* Ok, we've done all the scanning. 1518*753ac610SCharles Manning * Fix up the hard link chains. 1519*753ac610SCharles Manning * We have scanned all the objects, now it's time to add these 1520*753ac610SCharles Manning * hardlinks. 1521*753ac610SCharles Manning */ 1522*753ac610SCharles Manning yaffs_link_fixup(dev, &hard_list); 1523*753ac610SCharles Manning 1524*753ac610SCharles Manning yaffs_release_temp_buffer(dev, chunk_data); 1525*753ac610SCharles Manning 1526*753ac610SCharles Manning if (alloc_failed) 1527*753ac610SCharles Manning return YAFFS_FAIL; 1528*753ac610SCharles Manning 1529*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); 1530*753ac610SCharles Manning 1531*753ac610SCharles Manning return YAFFS_OK; 1532*753ac610SCharles Manning } 1533