yaffs2: Yaffs endian support
[yaffs2.git] / yaffs_guts.c
index 1c0ae71320a52fc5645e09fd76e5203c348ab8c7..72555d53ed08a6ec7864ddb5c598768fb140eda8 100644 (file)
@@ -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"
@@ -755,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;
@@ -1922,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;
@@ -2004,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;
@@ -2532,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;
 
@@ -2549,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);
@@ -3053,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);
 
@@ -3074,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);
 
@@ -3093,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;
-
 }
 
 
@@ -3131,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;
 
@@ -3181,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;
@@ -3235,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);
@@ -3250,7 +3264,18 @@ static void yaffs_check_obj_details_loaded(struct yaffs_obj *in)
 
 /* 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;
 }