X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_yaffs2.c;h=6d4681726a60b3ae63adc3456661a8652790194a;hp=77101e72e1bdda16913bc4ba3ac4e34f49251667;hb=21b2dedaa32ab309f6d1daec966528b7586bd207;hpb=511baf572f96c689043518d443086b8dd6751089 diff --git a/yaffs_yaffs2.c b/yaffs_yaffs2.c index 77101e7..6d46817 100644 --- a/yaffs_yaffs2.c +++ b/yaffs_yaffs2.c @@ -1,7 +1,7 @@ /* * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. * - * Copyright (C) 2002-2010 Aleph One Ltd. + * Copyright (C) 2002-2011 Aleph One Ltd. * for Toby Churchill Ltd and Brightstar Engineering * * Created by Charles Manning @@ -20,6 +20,8 @@ #include "yaffs_getblockinfo.h" #include "yaffs_verify.h" #include "yaffs_attribs.h" +#include "yaffs_summary.h" +#include "yaffs_endian.h" /* * Checkpoints are really no benefit on very small partitions. @@ -54,7 +56,8 @@ void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { if (b->block_state == YAFFS_BLOCK_STATE_FULL && (b->pages_in_use - b->soft_del_pages) < - dev->param.chunks_per_block && b->seq_number < seq) { + dev->param.chunks_per_block && + b->seq_number < seq) { seq = b->seq_number; block_no = i; } @@ -206,17 +209,16 @@ int yaffs2_checkpt_required(struct yaffs_dev *dev) int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) { int retval; + int n_bytes = 0; + int n_blocks; + int dev_blocks; if (!dev->param.is_yaffs2) return 0; if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { /* Not a valid value so recalculate */ - int n_bytes = 0; - int n_blocks; - int dev_blocks = - (dev->param.end_block - dev->param.start_block + 1); - + dev_blocks = dev->param.end_block - dev->param.start_block + 1; n_bytes += sizeof(struct yaffs_checkpt_validity); n_bytes += sizeof(struct yaffs_checkpt_dev); n_bytes += dev_blocks * sizeof(struct yaffs_block_info); @@ -247,6 +249,18 @@ int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) /*--------------------- Checkpointing --------------------*/ +static void yaffs2_do_endian_validity_marker(struct yaffs_dev *dev, + struct yaffs_checkpt_validity *v) +{ + + if (!dev->swap_endian) + return; + v->struct_type = swap_s32(v->struct_type); + v->magic = swap_u32(v->magic); + v->version = swap_u32(v->version); + v->head = swap_u32(v->head); +} + static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) { struct yaffs_checkpt_validity cp; @@ -258,6 +272,8 @@ static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) cp.version = YAFFS_CHECKPOINT_VERSION; cp.head = (head) ? 1 : 0; + yaffs2_do_endian_validity_marker(dev, &cp); + return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; } @@ -267,6 +283,7 @@ static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) int ok; ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); + yaffs2_do_endian_validity_marker(dev, &cp); if (ok) ok = (cp.struct_type == sizeof(cp)) && @@ -279,6 +296,8 @@ static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, struct yaffs_dev *dev) { + cp->struct_type = sizeof(*cp); + cp->n_erased_blocks = dev->n_erased_blocks; cp->alloc_block = dev->alloc_block; cp->alloc_page = dev->alloc_page; @@ -305,33 +324,66 @@ static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, dev->seq_number = cp->seq_number; } +static void yaffs2_do_endian_checkpt_dev(struct yaffs_dev *dev, + struct yaffs_checkpt_dev *cp) +{ + if (!dev->swap_endian) + return; + cp->struct_type = swap_s32(cp->struct_type); + cp->n_erased_blocks = swap_s32(cp->n_erased_blocks); + cp->alloc_block = swap_s32(cp->alloc_block); + cp->alloc_page = swap_u32(cp->alloc_page); + cp->n_free_chunks = swap_s32(cp->n_free_chunks); + cp->n_deleted_files = swap_s32(cp->n_deleted_files); + cp->n_unlinked_files = swap_s32(cp->n_unlinked_files); + cp->n_bg_deletions = swap_s32(cp->n_bg_deletions); +} + static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) { struct yaffs_checkpt_dev cp; u32 n_bytes; - u32 n_blocks = - (dev->internal_end_block - dev->internal_start_block + 1); + u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; int ok; + int i; + union yaffs_block_info_union bu; /* Write device runtime values */ yaffs2_dev_to_checkpt_dev(&cp, dev); - cp.struct_type = sizeof(cp); + yaffs2_do_endian_checkpt_dev(dev, &cp); ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); + if (!ok) + return 0; - /* Write block info */ - if (ok) { + /* Write block info. */ + if (!dev->swap_endian) { n_bytes = n_blocks * sizeof(struct yaffs_block_info); - ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == - n_bytes); + ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); + } else { + /* + * Need to swap the endianisms. We can't do this in place + * since that would damage live data, + * so write one block info at a time using a copy. + */ + for (i = 0; i < n_blocks && ok; i++) { + bu.bi = dev->block_info[i]; + bu.as_u32[0] = swap_u32(bu.as_u32[0]); + bu.as_u32[1] = swap_u32(bu.as_u32[1]); + ok = (yaffs2_checkpt_wr(dev, &bu, sizeof(bu)) == sizeof(bu)); + } } - /* Write chunk bits */ - if (ok) { - n_bytes = n_blocks * dev->chunk_bit_stride; - ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == - n_bytes); - } + if (!ok) + return 0; + + /* + * Write chunk bits. Chunk bits are in bytes so + * no endian conversion is needed. + */ + n_bytes = n_blocks * dev->chunk_bit_stride; + ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); + return ok ? 1 : 0; } @@ -346,6 +398,7 @@ static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); if (!ok) return 0; + yaffs2_do_endian_checkpt_dev(dev, &cp); if (cp.struct_type != sizeof(cp)) return 0; @@ -358,27 +411,66 @@ static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) if (!ok) return 0; + + if (dev->swap_endian) { + /* The block info can just be handled as a list of u32s. */ + u32 *as_u32 = (u32 *) dev->block_info; + u32 n_u32s = n_bytes/sizeof(u32); + u32 i; + + for (i=0; i < n_u32s; i++) + as_u32[i] = swap_u32(as_u32[i]); + } + n_bytes = n_blocks * dev->chunk_bit_stride; ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); + return ok ? 1 : 0; } + +static void yaffs2_checkpt_obj_bit_assign(struct yaffs_checkpt_obj *cp, + int bit_offset, + int bit_width, + u32 value) +{ + u32 and_mask; + + and_mask = ((1<bit_field &= ~and_mask; + cp->bit_field |= ((value << bit_offset) & and_mask); +} + +static u32 yaffs2_checkpt_obj_bit_get(struct yaffs_checkpt_obj *cp, + int bit_offset, + int bit_width) +{ + u32 and_mask; + + and_mask = ((1<bit_field >> bit_offset) & and_mask; +} + static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, struct yaffs_obj *obj) { cp->obj_id = obj->obj_id; cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; cp->hdr_chunk = obj->hdr_chunk; - cp->variant_type = obj->variant_type; - cp->deleted = obj->deleted; - cp->soft_del = obj->soft_del; - cp->unlinked = obj->unlinked; - cp->fake = obj->fake; - cp->rename_allowed = obj->rename_allowed; - cp->unlink_allowed = obj->unlink_allowed; - cp->serial = obj->serial; + + yaffs2_checkpt_obj_bit_assign(cp, CHECKPOINT_VARIANT_BITS, obj->variant_type); + yaffs2_checkpt_obj_bit_assign(cp, CHECKPOINT_DELETED_BITS, obj->deleted); + yaffs2_checkpt_obj_bit_assign(cp, CHECKPOINT_SOFT_DEL_BITS, obj->soft_del); + yaffs2_checkpt_obj_bit_assign(cp, CHECKPOINT_UNLINKED_BITS, obj->unlinked); + yaffs2_checkpt_obj_bit_assign(cp, CHECKPOINT_FAKE_BITS, obj->fake); + yaffs2_checkpt_obj_bit_assign(cp, CHECKPOINT_RENAME_ALLOWED_BITS, obj->rename_allowed); + yaffs2_checkpt_obj_bit_assign(cp, CHECKPOINT_UNLINK_ALLOWED_BITS, obj->unlink_allowed); + yaffs2_checkpt_obj_bit_assign(cp, CHECKPOINT_SERIAL_BITS, obj->serial); + cp->n_data_chunks = obj->n_data_chunks; if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) @@ -387,15 +479,16 @@ static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; } -static int taffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, +static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, struct yaffs_checkpt_obj *cp) { struct yaffs_obj *parent; + u32 cp_variant_type = yaffs2_checkpt_obj_bit_get(cp, CHECKPOINT_VARIANT_BITS); - if (obj->variant_type != cp->variant_type) { + if (obj->variant_type != cp_variant_type) { yaffs_trace(YAFFS_TRACE_ERROR, "Checkpoint read object %d type %d chunk %d does not match existing object type %d", - cp->obj_id, cp->variant_type, cp->hdr_chunk, + cp->obj_id, cp_variant_type, cp->hdr_chunk, obj->variant_type); return 0; } @@ -414,7 +507,7 @@ static int taffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, yaffs_trace(YAFFS_TRACE_ALWAYS, "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", cp->obj_id, cp->parent_id, - cp->variant_type, cp->hdr_chunk, + cp_variant_type, cp->hdr_chunk, parent->variant_type); return 0; } @@ -422,26 +515,56 @@ static int taffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, } obj->hdr_chunk = cp->hdr_chunk; - obj->variant_type = cp->variant_type; - obj->deleted = cp->deleted; - obj->soft_del = cp->soft_del; - obj->unlinked = cp->unlinked; - obj->fake = cp->fake; - obj->rename_allowed = cp->rename_allowed; - obj->unlink_allowed = cp->unlink_allowed; - obj->serial = cp->serial; + + obj->variant_type = yaffs2_checkpt_obj_bit_get(cp, CHECKPOINT_VARIANT_BITS); + obj->deleted = yaffs2_checkpt_obj_bit_get(cp, CHECKPOINT_DELETED_BITS); + obj->soft_del = yaffs2_checkpt_obj_bit_get(cp, CHECKPOINT_SOFT_DEL_BITS); + obj->unlinked = yaffs2_checkpt_obj_bit_get(cp, CHECKPOINT_UNLINKED_BITS); + obj->fake = yaffs2_checkpt_obj_bit_get(cp, CHECKPOINT_FAKE_BITS); + obj->rename_allowed = yaffs2_checkpt_obj_bit_get(cp, CHECKPOINT_RENAME_ALLOWED_BITS); + obj->unlink_allowed = yaffs2_checkpt_obj_bit_get(cp, CHECKPOINT_UNLINK_ALLOWED_BITS); + obj->serial = yaffs2_checkpt_obj_bit_get(cp, CHECKPOINT_SERIAL_BITS); + obj->n_data_chunks = cp->n_data_chunks; - if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) + if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) { obj->variant.file_variant.file_size = cp->size_or_equiv_obj; - else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) + obj->variant.file_variant.stored_size = cp->size_or_equiv_obj; + } else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; - + } if (obj->hdr_chunk > 0) obj->lazy_loaded = 1; return 1; } +static void yaffs2_do_endian_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) +{ + int i; + u32 *as_u32 = (u32 *)tn; + int tnode_size_u32 = dev->tnode_size / sizeof(u32); + + if (!dev->swap_endian) + return; + /* Swap all the tnode data as u32s to fix endianisms. */ + for (i = 0; iswap_endian) + return tn; + + memcpy(dev->tn_swap_buffer, tn, dev->tnode_size); + tn = dev->tn_swap_buffer; + + yaffs2_do_endian_tnode(dev, tn); + + return tn; +} + static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, u32 level, int chunk_offset) @@ -449,33 +572,40 @@ static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, int i; struct yaffs_dev *dev = in->my_dev; int ok = 1; - - if (tn) { - if (level > 0) { - - for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { - if (tn->internal[i]) { - ok = yaffs2_checkpt_tnode_worker(in, - tn->internal[i], - level - 1, - (chunk_offset << - YAFFS_TNODES_INTERNAL_BITS) - + i); - } - } - } else if (level == 0) { - u32 base_offset = - chunk_offset << YAFFS_TNODES_LEVEL0_BITS; - ok = (yaffs2_checkpt_wr - (dev, &base_offset, - sizeof(base_offset)) == sizeof(base_offset)); - if (ok) - ok = (yaffs2_checkpt_wr - (dev, tn, - dev->tnode_size) == dev->tnode_size); + u32 base_offset; + + if (!tn) + return 1; + + if (level > 0) { + for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { + if (!tn->internal[i]) + continue; + ok = yaffs2_checkpt_tnode_worker(in, + tn->internal[i], + level - 1, + (chunk_offset << + YAFFS_TNODES_INTERNAL_BITS) + i); } + return ok; } + /* Level 0 tnode */ + base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; + yaffs_do_endian_u32(dev, &base_offset); + + ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == + sizeof(base_offset)); + if (ok) { + /* + * NB Can't do an in-place endian swizzle since that would + * damage current tnode data. + * If a tnode endian conversion is required we do a copy. + */ + tn = yaffs2_do_endian_tnode_copy(dev, tn); + ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == + dev->tnode_size); + } return ok; } @@ -484,16 +614,16 @@ static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) u32 end_marker = ~0; int ok = 1; - if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) { - ok = yaffs2_checkpt_tnode_worker(obj, - obj->variant.file_variant.top, - obj->variant.file_variant. - top_level, 0); - if (ok) - ok = (yaffs2_checkpt_wr - (obj->my_dev, &end_marker, - sizeof(end_marker)) == sizeof(end_marker)); - } + if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) + return ok; + + ok = yaffs2_checkpt_tnode_worker(obj, + obj->variant.file_variant.top, + obj->variant.file_variant. + top_level, 0); + if (ok) + ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, + sizeof(end_marker)) == sizeof(end_marker)); return ok ? 1 : 0; } @@ -510,14 +640,18 @@ static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == sizeof(base_chunk)); + yaffs_do_endian_u32(dev, &base_chunk); + while (ok && (~base_chunk)) { nread++; /* Read level 0 tnode */ tn = yaffs_get_tnode(dev); - if (tn) + if (tn) { ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == dev->tnode_size); + yaffs2_do_endian_tnode(dev, tn); + } else ok = 0; @@ -526,10 +660,13 @@ static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) file_stuct_ptr, base_chunk, tn) ? 1 : 0; - if (ok) + if (ok) { ok = (yaffs2_checkpt_rd (dev, &base_chunk, sizeof(base_chunk)) == sizeof(base_chunk)); + yaffs_do_endian_u32(dev, &base_chunk); + } + } yaffs_trace(YAFFS_TRACE_CHECKPOINT, @@ -539,6 +676,21 @@ static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) return ok ? 1 : 0; } + +static void yaffs2_do_endian_checkpt_obj(struct yaffs_dev *dev, + struct yaffs_checkpt_obj *cp) +{ + if (!dev->swap_endian) + return; + cp->struct_type = swap_s32(cp->struct_type); + cp->obj_id = swap_u32(cp->obj_id); + cp->parent_id = swap_u32(cp->parent_id); + cp->hdr_chunk = swap_s32(cp->hdr_chunk); + cp->bit_field = swap_u32(cp->bit_field); + cp->n_data_chunks = swap_s32(cp->n_data_chunks); + cp->size_or_equiv_obj = swap_loff_t(cp->size_or_equiv_obj); +} + static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) { struct yaffs_obj *obj; @@ -546,6 +698,7 @@ static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) int i; int ok = 1; struct list_head *lh; + u32 cp_variant_type; /* Iterate through the objects in each hash entry, * dumping them to the checkpointing stream. @@ -557,12 +710,14 @@ static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) if (!obj->defered_free) { yaffs2_obj_checkpt_obj(&cp, obj); cp.struct_type = sizeof(cp); - + cp_variant_type = yaffs2_checkpt_obj_bit_get( + &cp, CHECKPOINT_VARIANT_BITS); yaffs_trace(YAFFS_TRACE_CHECKPOINT, "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", cp.obj_id, cp.parent_id, - cp.variant_type, cp.hdr_chunk, obj); + cp_variant_type, cp.hdr_chunk, obj); + yaffs2_do_endian_checkpt_obj (dev, &cp); ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); @@ -575,8 +730,9 @@ static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) } /* Dump end of list */ - memset(&cp, 0xFF, sizeof(struct yaffs_checkpt_obj)); + memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); cp.struct_type = sizeof(cp); + yaffs2_do_endian_checkpt_obj (dev, &cp); if (ok) ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); @@ -590,10 +746,14 @@ static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) struct yaffs_checkpt_obj cp; int ok = 1; int done = 0; - struct yaffs_obj *hard_list = NULL; + u32 cp_variant_type; + LIST_HEAD(hard_list); + while (ok && !done) { ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); + yaffs2_do_endian_checkpt_obj (dev, &cp); + if (cp.struct_type != sizeof(cp)) { yaffs_trace(YAFFS_TRACE_CHECKPOINT, "struct size %d instead of %d ok %d", @@ -601,9 +761,11 @@ static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) ok = 0; } + cp_variant_type = yaffs2_checkpt_obj_bit_get( + &cp, CHECKPOINT_VARIANT_BITS); yaffs_trace(YAFFS_TRACE_CHECKPOINT, "Checkpoint read object %d parent %d type %d chunk %d ", - cp.obj_id, cp.parent_id, cp.variant_type, + cp.obj_id, cp.parent_id, cp_variant_type, cp.hdr_chunk); if (ok && cp.obj_id == ~0) { @@ -611,9 +773,9 @@ static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) } else if (ok) { obj = yaffs_find_or_create_by_number(dev, cp.obj_id, - cp.variant_type); + cp_variant_type); if (obj) { - ok = taffs2_checkpt_obj_to_obj(obj, &cp); + ok = yaffs2_checkpt_obj_to_obj(obj, &cp); if (!ok) break; if (obj->variant_type == @@ -621,9 +783,7 @@ static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) ok = yaffs2_rd_checkpt_tnodes(obj); } else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { - obj->hard_links.next = - (struct list_head *)hard_list; - hard_list = obj; + list_add(&obj->hard_links, &hard_list); } } else { ok = 0; @@ -632,7 +792,7 @@ static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) } if (ok) - yaffs_link_fixup(dev, hard_list); + yaffs_link_fixup(dev, &hard_list); return ok ? 1 : 0; } @@ -644,6 +804,8 @@ static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) yaffs2_get_checkpt_sum(dev, &checkpt_sum); + yaffs_do_endian_u32(dev, &checkpt_sum); + ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == sizeof(checkpt_sum)); @@ -666,6 +828,7 @@ static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) if (!ok) return 0; + yaffs_do_endian_u32(dev, &checkpt_sum1); if (checkpt_sum0 != checkpt_sum1) return 0; @@ -830,6 +993,10 @@ int yaffs2_checkpt_restore(struct yaffs_dev *dev) return retval; } +/* End of checkpointing */ + +/* Hole handling logic for truncate past end of file */ + int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) { /* if new_size > old_file_size. @@ -838,7 +1005,7 @@ int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) * of hole marker. */ loff_t old_file_size; - int increase; + loff_t increase; int small_hole; int result = YAFFS_OK; struct yaffs_dev *dev = NULL; @@ -871,11 +1038,11 @@ int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) small_hole = 0; if (small_hole) - local_buffer = yaffs_get_temp_buffer(dev, __LINE__); + local_buffer = yaffs_get_temp_buffer(dev); if (local_buffer) { /* fill hole with zero bytes */ - int pos = old_file_size; + loff_t pos = old_file_size; int this_write; int written; memset(local_buffer, 0, dev->data_bytes_per_chunk); @@ -896,7 +1063,7 @@ int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) } } - yaffs_release_temp_buffer(dev, local_buffer, __LINE__); + yaffs_release_temp_buffer(dev, local_buffer); /* If out of space then reverse any chunks we've added */ if (!small_increase_ok) @@ -914,6 +1081,8 @@ int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) return result; } +/* Yaffs2 scanning */ + struct yaffs_block_index { int seq; int block; @@ -928,8 +1097,8 @@ static int yaffs2_ybicmp(const void *a, const void *b) if (aseq == bseq) return ablock - bblock; - else - return aseq - bseq; + + return aseq - bseq; } static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, @@ -937,13 +1106,14 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, int blk, int chunk_in_block, int *found_chunks, u8 *chunk_data, - struct yaffs_obj **hard_list) + struct list_head *hard_list, + int summary_available) { struct yaffs_obj_hdr *oh; struct yaffs_obj *in; struct yaffs_obj *parent; int equiv_id; - int file_size; + loff_t file_size; int is_shrink; int is_unlinked; struct yaffs_ext_tags tags; @@ -954,7 +1124,17 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, struct yaffs_hardlink_var *hl_var; struct yaffs_symlink_var *sl_var; - result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); + if (summary_available) { + result = yaffs_summary_fetch(dev, &tags, chunk_in_block); + tags.seq_number = bi->seq_number; + } + + if (!summary_available || tags.obj_id == 0) { + result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); + dev->tags_used++; + } else { + dev->summary_used++; + } /* Let's have a good look at this chunk... */ @@ -979,8 +1159,8 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, bi->block_state = YAFFS_BLOCK_STATE_EMPTY; dev->n_erased_blocks++; } else { - if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCANNING - || bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { + if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { if (dev->seq_number == bi->seq_number) { /* Allocating from this block*/ yaffs_trace(YAFFS_TRACE_SCAN, @@ -1014,6 +1194,7 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, dev->n_free_chunks++; } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || tags.chunk_id > YAFFS_MAX_CHUNK_ID || + tags.obj_id == YAFFS_OBJECTID_SUMMARY || (tags.chunk_id > 0 && tags.n_bytes > dev->data_bytes_per_chunk) || tags.seq_number != bi->seq_number) { @@ -1024,8 +1205,8 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, dev->n_free_chunks++; } else if (tags.chunk_id > 0) { /* chunk_id > 0 so it is a data chunk... */ - unsigned int endpos; - u32 chunk_base = (tags.chunk_id - 1) * + loff_t endpos; + loff_t chunk_base = (tags.chunk_id - 1) * dev->data_bytes_per_chunk; *found_chunks = 1; @@ -1058,9 +1239,9 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, endpos = chunk_base + tags.n_bytes; if (!in->valid && - in->variant.file_variant.scanned_size < endpos) { + in->variant.file_variant.stored_size < endpos) { in->variant.file_variant. - scanned_size = endpos; + stored_size = endpos; in->variant.file_variant. file_size = endpos; } @@ -1092,12 +1273,10 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, } if (!in || - (!in->valid && dev->param.disable_lazy_load) - || tags.extra_shadows || - (!in->valid && (tags.obj_id == - YAFFS_OBJECTID_ROOT - || tags.obj_id == - YAFFS_OBJECTID_LOSTNFOUND))) { + (!in->valid && dev->param.disable_lazy_load) || + tags.extra_shadows || + (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || + tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { /* If we don't have valid info then we * need to read the chunk @@ -1113,6 +1292,8 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, oh = (struct yaffs_obj_hdr *)chunk_data; + yaffs_do_endian_oh(dev, oh); + if (dev->param.inband_tags) { /* Fix up the header if they got * corrupted by inband tags */ @@ -1149,9 +1330,9 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, (tags.extra_available && tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) )) { - u32 this_size = (oh) ? - oh->file_size : - tags.extra_length; + loff_t this_size = (oh) ? + yaffs_oh_to_size(dev, oh, 0) : + tags.extra_file_size; u32 parent_obj_id = (oh) ? oh->parent_obj_id : tags.extra_parent_id; @@ -1185,12 +1366,14 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, } if (!in->valid && in->variant_type != - (oh ? oh->type : tags.extra_obj_type)) + (oh ? oh->type : tags.extra_obj_type)) { yaffs_trace(YAFFS_TRACE_ERROR, - "yaffs tragedy: Bad object type, %d != %d, for object %d at chunk %d during scan", + "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", oh ? oh->type : tags.extra_obj_type, in->variant_type, tags.obj_id, chunk); + in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); + } if (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || @@ -1225,7 +1408,7 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, parent = yaffs_find_or_create_by_number(dev, oh->parent_obj_id, YAFFS_OBJECT_TYPE_DIRECTORY); - file_size = oh->file_size; + file_size = yaffs_oh_to_size(dev, oh, 0); is_shrink = oh->is_shrink; equiv_id = oh->equiv_id; } else { @@ -1233,7 +1416,7 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, parent = yaffs_find_or_create_by_number(dev, tags.extra_parent_id, YAFFS_OBJECT_TYPE_DIRECTORY); - file_size = tags.extra_length; + file_size = tags.extra_file_size; is_shrink = tags.extra_is_shrink; equiv_id = tags.extra_equiv_id; in->lazy_loaded = 1; @@ -1289,7 +1472,7 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, break; case YAFFS_OBJECT_TYPE_FILE: file_var = &in->variant.file_variant; - if (file_var->scanned_size < file_size) { + if (file_var->stored_size < file_size) { /* This covers the case where the file * size is greater than the data held. * This will happen if the file is @@ -1297,7 +1480,7 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, * current data extents. */ file_var->file_size = file_size; - file_var->scanned_size = file_size; + file_var->stored_size = file_size; } if (file_var->shrink_size > file_size) @@ -1308,9 +1491,7 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, hl_var = &in->variant.hardlink_variant; if (!is_unlinked) { hl_var->equiv_id = equiv_id; - in->hard_links.next = - (struct list_head *) *hard_list; - *hard_list = in; + list_add(&in->hard_links, hard_list); } break; case YAFFS_OBJECT_TYPE_DIRECTORY: @@ -1343,8 +1524,7 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) int n_to_scan = 0; enum yaffs_block_state state; int c; - int deleted; - struct yaffs_obj *hard_list = NULL; + LIST_HEAD(hard_list); struct yaffs_block_info *bi; u32 seq_number; int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; @@ -1353,6 +1533,7 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) int alloc_failed = 0; struct yaffs_block_index *block_index = NULL; int alt_block_index = 0; + int summary_available; yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", @@ -1360,8 +1541,8 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; - block_index = kmalloc(n_blocks * sizeof(struct yaffs_block_index), - GFP_NOFS); + block_index = + kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); if (!block_index) { block_index = @@ -1378,7 +1559,7 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) dev->blocks_in_checkpt = 0; - chunk_data = yaffs_get_temp_buffer(dev, __LINE__); + chunk_data = yaffs_get_temp_buffer(dev); /* Scan all the blocks to determine their state */ bi = dev->block_info; @@ -1413,7 +1594,7 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) dev->n_erased_blocks++; dev->n_free_chunks += dev->param.chunks_per_block; } else if (bi->block_state == - YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + YAFFS_BLOCK_STATE_NEEDS_SCAN) { /* Determine the highest sequence number */ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { @@ -1432,7 +1613,7 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) bi++; } - yaffs_trace(YAFFS_TRACE_SCAN, "%d blocks to be sorted...", n_to_scan); + yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); cond_resched(); @@ -1450,7 +1631,8 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); /* For each block.... backwards */ - for (block_iter = end_iter; !alloc_failed && block_iter >= start_iter; + for (block_iter = end_iter; + !alloc_failed && block_iter >= start_iter; block_iter--) { /* Cooperative multitasking! This loop can run for so long that watchdog timers expire. */ @@ -1459,25 +1641,32 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) /* get the block to scan in the correct order */ blk = block_index[block_iter].block; bi = yaffs_get_block_info(dev, blk); - deleted = 0; + + summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); /* For each chunk in each block that needs scanning.... */ found_chunks = 0; - for (c = dev->param.chunks_per_block - 1; + if (summary_available) + c = dev->chunks_per_summary - 1; + else + c = dev->param.chunks_per_block - 1; + + for (/* c is already initialised */; !alloc_failed && c >= 0 && - (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || - bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); c--) { + (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); + c--) { /* Scan backwards... * Read the tags and decide what to do */ if (yaffs2_scan_chunk(dev, bi, blk, c, &found_chunks, chunk_data, - &hard_list) == - YAFFS_FAIL) + &hard_list, summary_available) == + YAFFS_FAIL) alloc_failed = 1; } - if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { /* If we got this far while scanning, then the block * is fully allocated. */ bi->block_state = YAFFS_BLOCK_STATE_FULL; @@ -1503,9 +1692,9 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) * We have scanned all the objects, now it's time to add these * hardlinks. */ - yaffs_link_fixup(dev, hard_list); + yaffs_link_fixup(dev, &hard_list); - yaffs_release_temp_buffer(dev, chunk_data, __LINE__); + yaffs_release_temp_buffer(dev, chunk_data); if (alloc_failed) return YAFFS_FAIL;