X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_guts.c;h=72555d53ed08a6ec7864ddb5c598768fb140eda8;hp=6a12aba23ebdaf89543980b3cf0fda1c4805a9ba;hb=21b2dedaa32ab309f6d1daec966528b7586bd207;hpb=30f956c32c235e6b5fa77fb29965ababbd497561 diff --git a/yaffs_guts.c b/yaffs_guts.c index 6a12aba..72555d5 100644 --- a/yaffs_guts.c +++ b/yaffs_guts.c @@ -15,6 +15,7 @@ #include "yaffs_trace.h" #include "yaffs_guts.h" +#include "yaffs_endian.h" #include "yaffs_getblockinfo.h" #include "yaffs_tagscompat.h" #include "yaffs_tagsmarshall.h" @@ -631,6 +632,78 @@ static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) /*---------------- Name handling functions ------------*/ +static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, + const YCHAR *oh_name, int buff_size) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + if (dev->param.auto_unicode) { + if (*oh_name) { + /* It is an ASCII name, do an ASCII to + * unicode conversion */ + const char *ascii_oh_name = (const char *)oh_name; + int n = buff_size - 1; + while (n > 0 && *ascii_oh_name) { + *name = *ascii_oh_name; + name++; + ascii_oh_name++; + n--; + } + } else { + strncpy(name, oh_name + 1, buff_size - 1); + } + } else { +#else + (void) dev; + { +#endif + strncpy(name, oh_name, buff_size - 1); + } +} + +static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, + const YCHAR *name) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + + int is_ascii; + const YCHAR *w; + + if (dev->param.auto_unicode) { + + is_ascii = 1; + w = name; + + /* Figure out if the name will fit in ascii character set */ + while (is_ascii && *w) { + if ((*w) & 0xff00) + is_ascii = 0; + w++; + } + + if (is_ascii) { + /* It is an ASCII name, so convert unicode to ascii */ + char *ascii_oh_name = (char *)oh_name; + int n = YAFFS_MAX_NAME_LENGTH - 1; + while (n > 0 && *name) { + *ascii_oh_name = *name; + name++; + ascii_oh_name++; + n--; + } + } else { + /* Unicode name, so save starting at the second YCHAR */ + *oh_name = 0; + strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); + } + } else { +#else + dev = dev; + { +#endif + strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); + } +} + static u16 yaffs_calc_name_sum(const YCHAR *name) { u16 sum = 0; @@ -683,7 +756,7 @@ void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, loff_t yaffs_max_file_size(struct yaffs_dev *dev) { - if(sizeof(loff_t) < 8) + if (sizeof(loff_t) < 8) return YAFFS_MAX_FILE_SIZE_32; else return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; @@ -1850,21 +1923,18 @@ static int yaffs_new_obj_id(struct yaffs_dev *dev) struct list_head *i; u32 n = (u32) bucket; - /* Now find an object value that has not already been taken - * by scanning the list. + /* + * Now find an object value that has not already been taken + * by scanning the list, incrementing each time by number of buckets. */ - while (!found) { found = 1; n += YAFFS_NOBJECT_BUCKETS; - if (1 || dev->obj_bucket[bucket].count > 0) { - list_for_each(i, &dev->obj_bucket[bucket].list) { - /* If there is already one in the list */ - if (i && list_entry(i, struct yaffs_obj, - hash_link)->obj_id == n) { - found = 0; - } - } + list_for_each(i, &dev->obj_bucket[bucket].list) { + /* Check if this value is already taken. */ + if (i && list_entry(i, struct yaffs_obj, + hash_link)->obj_id == n) + found = 0; } } return n; @@ -1932,7 +2002,7 @@ static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, switch (type) { case YAFFS_OBJECT_TYPE_FILE: the_obj->variant.file_variant.file_size = 0; - the_obj->variant.file_variant.scanned_size = 0; + the_obj->variant.file_variant.stored_size = 0; the_obj->variant.file_variant.shrink_size = yaffs_max_file_size(dev); the_obj->variant.file_variant.top_level = 0; @@ -2460,12 +2530,14 @@ static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, if (tags.chunk_id == 0) { /* It is an object Id, - * We need to nuke the - * shrinkheader flags since its + * We need to nuke the shrinkheader flags since its * work is done. - * Also need to clean up - * shadowing. + * Also need to clean up shadowing. + * NB We don't want to do all the work of translating + * object header endianism back and forth so we leave + * the oh endian in its stored order. */ + struct yaffs_obj_hdr *oh; oh = (struct yaffs_obj_hdr *) buffer; @@ -2477,10 +2549,10 @@ static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, /* Update file size */ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { - yaffs_oh_size_load(oh, - object->variant.file_variant.file_size); + yaffs_oh_size_load(dev, oh, + object->variant.file_variant.stored_size, 1); tags.extra_file_size = - object->variant.file_variant.file_size; + object->variant.file_variant.stored_size; } yaffs_verify_oh(object, oh, &tags, 1); @@ -2981,6 +3053,7 @@ static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, int new_chunk_id; struct yaffs_ext_tags new_tags; struct yaffs_dev *dev = in->my_dev; + loff_t endpos; yaffs_check_gc(dev, 0); @@ -3002,13 +3075,24 @@ static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; new_tags.n_bytes = n_bytes; - if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { + if (n_bytes < 1 || n_bytes > dev->data_bytes_per_chunk) { yaffs_trace(YAFFS_TRACE_ERROR, "Writing %d bytes to chunk!!!!!!!!!", n_bytes); BUG(); } + /* + * If this is a data chunk and the write goes past the end of the stored + * size then update the stored_size. + */ + if (inode_chunk > 0) { + endpos = (inode_chunk - 1) * dev->data_bytes_per_chunk + + n_bytes; + if (in->variant.file_variant.stored_size < endpos) + in->variant.file_variant.stored_size = endpos; + } + new_chunk_id = yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); @@ -3021,7 +3105,6 @@ static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, yaffs_verify_file_sane(in); } return new_chunk_id; - } @@ -3059,12 +3142,12 @@ static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, if (xmod->set) retval = - nval_set(x_buffer, x_size, xmod->name, xmod->data, + nval_set(dev, x_buffer, x_size, xmod->name, xmod->data, xmod->size, xmod->flags); else - retval = nval_del(x_buffer, x_size, xmod->name); + retval = nval_del(dev, x_buffer, x_size, xmod->name); - obj->has_xattr = nval_hasvalues(x_buffer, x_size); + obj->has_xattr = nval_hasvalues(dev, x_buffer, x_size); obj->xattr_known = 1; xmod->result = retval; @@ -3109,14 +3192,15 @@ static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, x_buffer = buffer + x_offs; if (!obj->xattr_known) { - obj->has_xattr = nval_hasvalues(x_buffer, x_size); + obj->has_xattr = nval_hasvalues(dev, x_buffer, x_size); obj->xattr_known = 1; } if (name) - retval = nval_get(x_buffer, x_size, name, value, size); + retval = nval_get(dev, x_buffer, x_size, + name, value, size); else - retval = nval_list(x_buffer, x_size, value, size); + retval = nval_list(dev, x_buffer, x_size, value, size); } yaffs_release_temp_buffer(dev, (u8 *) buffer); return retval; @@ -3163,6 +3247,8 @@ static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); oh = (struct yaffs_obj_hdr *)buf; + yaffs_do_endian_oh(dev, oh); + in->yst_mode = oh->yst_mode; yaffs_load_attribs(in, oh); yaffs_set_obj_name_from_oh(in, oh); @@ -3176,81 +3262,20 @@ static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) yaffs_release_temp_buffer(dev, buf); } -static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, - const YCHAR *oh_name, int buff_size) -{ -#ifdef CONFIG_YAFFS_AUTO_UNICODE - if (dev->param.auto_unicode) { - if (*oh_name) { - /* It is an ASCII name, do an ASCII to - * unicode conversion */ - const char *ascii_oh_name = (const char *)oh_name; - int n = buff_size - 1; - while (n > 0 && *ascii_oh_name) { - *name = *ascii_oh_name; - name++; - ascii_oh_name++; - n--; - } - } else { - strncpy(name, oh_name + 1, buff_size - 1); - } - } else { -#else - (void) dev; - { -#endif - strncpy(name, oh_name, buff_size - 1); - } -} - -static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, - const YCHAR *name) -{ -#ifdef CONFIG_YAFFS_AUTO_UNICODE - - int is_ascii; - YCHAR *w; - - if (dev->param.auto_unicode) { - - is_ascii = 1; - w = name; - - /* Figure out if the name will fit in ascii character set */ - while (is_ascii && *w) { - if ((*w) & 0xff00) - is_ascii = 0; - w++; - } - - if (is_ascii) { - /* It is an ASCII name, so convert unicode to ascii */ - char *ascii_oh_name = (char *)oh_name; - int n = YAFFS_MAX_NAME_LENGTH - 1; - while (n > 0 && *name) { - *ascii_oh_name = *name; - name++; - ascii_oh_name++; - n--; - } - } else { - /* Unicode name, so save starting at the second YCHAR */ - *oh_name = 0; - strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); - } - } else { -#else - dev = dev; - { -#endif - strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); - } -} - /* UpdateObjectHeader updates the header on NAND for an object. * If name is not NULL, then that new name is used. + * + * We're always creating the obj header from scratch (except reading + * the old name) so first set up in cpu endianness then run it through + * endian fixing at the end. + * + * However, a twist: If there are xattribs we leave them as they were. + * + * Careful! The buffer holds the whole chunk. Part of the chunk holds the + * object header and the rest holds the xattribs, therefore we use a buffer + * pointer and an oh pointer to point to the same memory. */ + int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) { @@ -3283,12 +3308,18 @@ int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, prev_chunk_id = in->hdr_chunk; if (prev_chunk_id > 0) { + /* Access the old obj header just to read the name. */ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, buffer, &old_tags); yaffs_verify_oh(in, oh, &old_tags, 0); memcpy(old_name, oh->name, sizeof(oh->name)); - memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); + + /* + * NB We only wipe the object header area because the rest of + * the buffer might contain xattribs. + */ + memset(oh, 0xff, sizeof(*oh)); } else { memset(buffer, 0xff, dev->data_bytes_per_chunk); } @@ -3322,8 +3353,8 @@ int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, case YAFFS_OBJECT_TYPE_FILE: if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) - file_size = in->variant.file_variant.file_size; - yaffs_oh_size_load(oh, file_size); + file_size = in->variant.file_variant.stored_size; + yaffs_oh_size_load(dev, oh, file_size, 0); break; case YAFFS_OBJECT_TYPE_HARDLINK: oh->equiv_id = in->variant.hardlink_variant.equiv_id; @@ -3362,6 +3393,10 @@ int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, new_tags.extra_equiv_id = oh->equiv_id; new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; new_tags.extra_obj_type = in->variant_type; + + /* Now endian swizzle the oh if needed. */ + yaffs_do_endian_oh(dev, oh); + yaffs_verify_oh(in, oh, &new_tags, 1); /* Create new chunk in NAND */ @@ -3743,6 +3778,7 @@ void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) } obj->variant.file_variant.file_size = new_size; + obj->variant.file_variant.stored_size = new_size; yaffs_prune_tree(dev, &obj->variant.file_variant); } @@ -4083,7 +4119,7 @@ static int yaffs_unlink_worker(struct yaffs_obj *obj) } } -static int yaffs_unlink_obj(struct yaffs_obj *obj) +int yaffs_unlink_obj(struct yaffs_obj *obj) { if (obj && obj->unlink_allowed) return yaffs_unlink_worker(obj); @@ -4626,8 +4662,11 @@ static int yaffs_check_dev_fns(struct yaffs_dev *dev) static int yaffs_create_initial_dir(struct yaffs_dev *dev) { /* Initialise the unlinked, deleted, root and lost+found directories */ - dev->lost_n_found = dev->root_dir = NULL; - dev->unlinked_dir = dev->del_dir = NULL; + dev->lost_n_found = NULL; + dev->root_dir = NULL; + dev->unlinked_dir = NULL; + dev->del_dir = NULL; + dev->unlinked_dir = yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); dev->del_dir = @@ -4639,9 +4678,15 @@ static int yaffs_create_initial_dir(struct yaffs_dev *dev) yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, YAFFS_LOSTNFOUND_MODE | S_IFDIR); - if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir - && dev->del_dir) { - yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); + if (dev->lost_n_found && + dev->root_dir && + dev->unlinked_dir && + dev->del_dir) { + /* If lost-n-found is hidden then yank it out of the directory tree. */ + if (dev->param.hide_lost_n_found) + list_del_init(&dev->lost_n_found->siblings); + else + yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); return YAFFS_OK; } return YAFFS_FAIL; @@ -4854,12 +4899,14 @@ int yaffs_guts_initialise(struct yaffs_dev *dev) dev->n_erase_failures = 0; dev->n_erased_blocks = 0; dev->gc_disable = 0; - dev->has_pending_prioritised_gc = 1; - /* Assume the worst for now, will get fixed on first GC */ + dev->has_pending_prioritised_gc = 1; /* Assume the worst for now, + * will get fixed on first GC */ INIT_LIST_HEAD(&dev->dirty_dirs); dev->oldest_dirty_seq = 0; dev->oldest_dirty_block = 0; + yaffs_endian_config(dev); + /* Initialise temporary buffers and caches. */ if (!yaffs_init_tmp_buffers(dev)) init_failed = 1; @@ -5022,8 +5069,15 @@ void yaffs_deinitialise(struct yaffs_dev *dev) kfree(dev->gc_cleanup_list); - for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { kfree(dev->temp_buffer[i].buffer); + dev->temp_buffer[i].buffer = NULL; + } + + kfree(dev->checkpt_buffer); + dev->checkpt_buffer = NULL; + kfree(dev->checkpt_block_list); + dev->checkpt_block_list = NULL; dev->is_mounted = 0; @@ -5091,26 +5145,47 @@ int yaffs_get_n_free_chunks(struct yaffs_dev *dev) } - /* * Marshalling functions to get loff_t file sizes into and out of * object headers. */ -void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) +void yaffs_oh_size_load(struct yaffs_dev *dev, + struct yaffs_obj_hdr *oh, + loff_t fsize, + int do_endian) { oh->file_size_low = (fsize & 0xFFFFFFFF); oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); + + if (do_endian) { + yaffs_do_endian_u32(dev, &oh->file_size_low); + yaffs_do_endian_u32(dev, &oh->file_size_high); + } } -loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) +loff_t yaffs_oh_to_size(struct yaffs_dev *dev, struct yaffs_obj_hdr *oh, + int do_endian) { loff_t retval; - if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) - retval = (((loff_t) oh->file_size_high) << 32) | - (((loff_t) oh->file_size_low) & 0xFFFFFFFF); - else - retval = (loff_t) oh->file_size_low; + + if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) { + u32 low = oh->file_size_low; + u32 high = oh->file_size_high; + + if (do_endian) { + yaffs_do_endian_u32 (dev, &low); + yaffs_do_endian_u32 (dev, &high); + } + retval = (((loff_t) high) << 32) | + (((loff_t) low) & 0xFFFFFFFF); + } else { + u32 low = oh->file_size_low; + + if (do_endian) + yaffs_do_endian_u32(dev, &low); + retval = (loff_t)low; + } return retval; }