From aa39fbf7616e8b26833a09cb64c6a40859494599 Mon Sep 17 00:00:00 2001
From: charles
Date: Mon, 12 Aug 2002 23:45:18 +0000
Subject: [PATCH] *** empty log message ***
---
Documentation/yaffs-todo.html | 30 +-
devextras.h | 524 +--
snMakefile | 4 +-
utils/Makefile | 22 +-
utils/mkyaffs | Bin 6382 -> 15115 bytes
utils/mkyaffsimage.c | 440 +++
yaffs_fileem.c | 32 +-
yaffs_fs.c | 18 +-
yaffs_guts.c | 6838 +++++++++++++++++----------------
yaffs_guts.h | 910 ++---
yaffsdev | Bin 104883 -> 109461 bytes
yaffsdev.c | 163 +-
yaffsdev.proj | Bin 36864 -> 36864 bytes
yportenv.h | 192 +-
14 files changed, 5077 insertions(+), 4096 deletions(-)
create mode 100644 utils/mkyaffsimage.c
diff --git a/Documentation/yaffs-todo.html b/Documentation/yaffs-todo.html
index 8489d72..59ebd0a 100644
--- a/Documentation/yaffs-todo.html
+++ b/Documentation/yaffs-todo.html
@@ -7,18 +7,24 @@
-
+
-YAFFS Todo as at 08/06/2002
+YAFFS Todo as at 09/07/2002
Stuff not yet done
Directory locking during directory walk.
- Other locking....
- Test disk full condition. Might not be being handled
- correctly.
- Add write memory mapping. We probably need this to support
+
Other locking.... investigate what is needed.
+ Test disk full condition.... investigate might not be being
+ handled correctly.
+ Add write memory mapping. We probably need this to support
loop mounting.
+ At scan time, check that the file size in the ObjectHeader
+ matches the filesize of the scanned blocks.
+
+Tools to be done
+
+ -
Recently done with no known problems (ie. probably needs
significant testing)
@@ -36,6 +42,16 @@ significant testing)
mot such a good thing to do with newer NANDs which do not like this
sort of thing.
+
nothing.
@@ -44,7 +60,7 @@ significant testing)
Discuss improved NAND page interface with mtd group. This has
actually started.
- Pull out all YAFFS_OK and YAFFS_FAIL style errors and return
+
Pull out all YAFFS_OK and YAFFS_FAIL style errors and return
with -ENOMEM style error messages.
diff --git a/devextras.h b/devextras.h
index a28135f..24fe5be 100644
--- a/devextras.h
+++ b/devextras.h
@@ -1,257 +1,267 @@
-/*
- * YAFFS: Yet another FFS. A NAND-flash specific file system.
- * devextras.h
- *
- * Copyright (C) 2002 Aleph One Ltd.
- * for Toby Churchill Ltd and Brightstar Engineering
- *
- * Created by Charles Manning
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This file is just holds extra declarations used during development.
- * Most of these are from kernel includes placed here so we can use them in
- * applications.
- *
- */
-
-#ifndef __EXTRAS_H__
-#define __EXTRAS_H__
-
-#ifndef __KERNEL__
-
-// User space defines
-
-typedef unsigned char __u8;
-typedef unsigned short __u16;
-typedef unsigned __u32;
-
-
-/*
- * Simple doubly linked list implementation.
- *
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
- #define prefetch(x) 1
-
-
-struct list_head {
- struct list_head *next, *prev;
-};
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
-
-#define INIT_LIST_HEAD(ptr) do { \
- (ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static __inline__ void __list_add(struct list_head * new,
- struct list_head * prev,
- struct list_head * next)
-{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
-}
-
-/**
- * list_add - add a new entry
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
- */
-static __inline__ void list_add(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head, head->next);
-}
-
-/**
- * list_add_tail - add a new entry
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- */
-static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head->prev, head);
-}
-
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static __inline__ void __list_del(struct list_head * prev,
- struct list_head * next)
-{
- next->prev = prev;
- prev->next = next;
-}
-
-/**
- * list_del - deletes entry from list.
- * @entry: the element to delete from the list.
- * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
- */
-static __inline__ void list_del(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
-}
-
-/**
- * list_del_init - deletes entry from list and reinitialize it.
- * @entry: the element to delete from the list.
- */
-static __inline__ void list_del_init(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- INIT_LIST_HEAD(entry);
-}
-
-/**
- * list_empty - tests whether a list is empty
- * @head: the list to test.
- */
-static __inline__ int list_empty(struct list_head *head)
-{
- return head->next == head;
-}
-
-/**
- * list_splice - join two lists
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- */
-static __inline__ void list_splice(struct list_head *list, struct list_head *head)
-{
- struct list_head *first = list->next;
-
- if (first != list) {
- struct list_head *last = list->prev;
- struct list_head *at = head->next;
-
- first->prev = head;
- head->next = first;
-
- last->next = at;
- at->prev = last;
- }
-}
-
-/**
- * list_entry - get the struct for this entry
- * @ptr: the &struct list_head pointer.
- * @type: the type of the struct this is embedded in.
- * @member: the name of the list_struct within the struct.
- */
-#define list_entry(ptr, type, member) \
- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-
-/**
- * list_for_each - iterate over a list
- * @pos: the &struct list_head to use as a loop counter.
- * @head: the head for your list.
- */
-#define list_for_each(pos, head) \
- for (pos = (head)->next, prefetch(pos->next); pos != (head); \
- pos = pos->next, prefetch(pos->next))
-
-/**
- * list_for_each_safe - iterate over a list safe against removal of list entry
- * @pos: the &struct list_head to use as a loop counter.
- * @n: another &struct list_head to use as temporary storage
- * @head: the head for your list.
- */
-#define list_for_each_safe(pos, n, head) \
- for (pos = (head)->next, n = pos->next; pos != (head); \
- pos = n, n = pos->next)
-
-
-
-#define CURRENT_TIME 0
-
-
-/*
- * File types
- */
-#define DT_UNKNOWN 0
-#define DT_FIFO 1
-#define DT_CHR 2
-#define DT_DIR 4
-#define DT_BLK 6
-#define DT_REG 8
-#define DT_LNK 10
-#define DT_SOCK 12
-#define DT_WHT 14
-
-#include
-
-/*
- * Attribute flags. These should be or-ed together to figure out what
- * has been changed!
- */
-#define ATTR_MODE 1
-#define ATTR_UID 2
-#define ATTR_GID 4
-#define ATTR_SIZE 8
-#define ATTR_ATIME 16
-#define ATTR_MTIME 32
-#define ATTR_CTIME 64
-#define ATTR_ATIME_SET 128
-#define ATTR_MTIME_SET 256
-#define ATTR_FORCE 512 /* Not a change, but a change it */
-#define ATTR_ATTR_FLAG 1024
-
-
-struct iattr {
- unsigned int ia_valid;
- unsigned ia_mode;
- unsigned ia_uid;
- unsigned ia_gid;
- unsigned ia_size;
- unsigned ia_atime;
- unsigned ia_mtime;
- unsigned ia_ctime;
- unsigned int ia_attr_flags;
-};
-
-#define KERN_DEBUG
-
-
-#else
-#include
-#include
-#include
-#include
-
-
-#endif
-
-
-
-
-#endif
-
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ * devextras.h
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file is just holds extra declarations used during development.
+ * Most of these are from kernel includes placed here so we can use them in
+ * applications.
+ *
+ */
+
+#ifndef __EXTRAS_H__
+#define __EXTRAS_H__
+
+#if defined WIN32
+#define __inline__ __inline
+#define new newHack
+#endif
+
+#if !(defined __KERNEL__) || (defined WIN32)
+
+// User space defines
+
+typedef unsigned char __u8;
+typedef unsigned short __u16;
+typedef unsigned __u32;
+
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+ #define prefetch(x) 1
+
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+
+
+
+/*
+ * File types
+ */
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+#define DT_WHT 14
+
+#ifndef WIN32
+#include
+#endif
+
+/*
+ * Attribute flags. These should be or-ed together to figure out what
+ * has been changed!
+ */
+#define ATTR_MODE 1
+#define ATTR_UID 2
+#define ATTR_GID 4
+#define ATTR_SIZE 8
+#define ATTR_ATIME 16
+#define ATTR_MTIME 32
+#define ATTR_CTIME 64
+#define ATTR_ATIME_SET 128
+#define ATTR_MTIME_SET 256
+#define ATTR_FORCE 512 /* Not a change, but a change it */
+#define ATTR_ATTR_FLAG 1024
+
+
+struct iattr {
+ unsigned int ia_valid;
+ unsigned ia_mode;
+ unsigned ia_uid;
+ unsigned ia_gid;
+ unsigned ia_size;
+ unsigned ia_atime;
+ unsigned ia_mtime;
+ unsigned ia_ctime;
+ unsigned int ia_attr_flags;
+};
+
+#define KERN_DEBUG
+
+
+#else
+
+#ifndef WIN32
+#include
+#include
+#include
+#include
+#endif
+
+#endif
+
+
+
+#if defined WIN32
+#undef new
+#endif
+
+#endif
+
diff --git a/snMakefile b/snMakefile
index 0872820..1573c7b 100644
--- a/snMakefile
+++ b/snMakefile
@@ -1,6 +1,6 @@
#########################################################
# Makefile auto generated by Cygnus Source Navigator.
-# Target: yaffsdev_disk Date: Jun 05 2002 Time: 02:41:55 PM
+# Target: yaffsdev_disk Date: Aug 09 2002 Time: 09:26:40 AM
#
@@ -72,7 +72,7 @@ yaffsdev: $(yaffsdev_disk_OBJECTS)
nand_ecc.o: /opt/yaffs/yportenv.h
yaffs_fileem.o: /opt/2.4.18/linux/include/linux/stat.h /opt/2.4.18/linux/include/linux/types.h /usr/include/fcntl.h /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h /usr/include/unistd.h /opt/yaffs/yaffs_fileem.h /opt/yaffs/yaffs_guts.h /opt/yaffs/yaffsinterface.h
yaffs_guts.o: /opt/yaffs/yaffs_guts.h /opt/yaffs/yaffsinterface.h /opt/yaffs/yportenv.h
-yaffsdev.o: yaffs_nandemul.h /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h /opt/yaffs/yaffs_fileem.h /opt/yaffs/yaffs_guts.h /opt/yaffs/yaffsinterface.h
+yaffsdev.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h /opt/yaffs/yaffs_fileem.h /opt/yaffs/yaffs_guts.h /opt/yaffs/yaffs_nandemul.h /opt/yaffs/yaffsinterface.h /opt/yaffs/yportenv.h
clean:
rm -f *.o
diff --git a/utils/Makefile b/utils/Makefile
index fdc64a3..a628641 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -16,19 +16,27 @@
KERNELDIR = /usr/src/kernel-headers-2.4.18
-CFLAGS = -I$(KERNELDIR)/include -O2 -Wall
+CFLAGS = -I$(KERNELDIR)/include -I.. -O2 -Wall
-OBJS = mkyaffs.o
+MKYAFFSOBJS = mkyaffs.o
+MKYAFFSIMAGEOBJS = mkyaffsimage.o nand_ecc.o
-all: mkyaffs
-$(OBJS): %.o: %.c
+all: mkyaffs mkyaffsimage
+
+$(MKYAFFSIMAGEOBJS): %.o: %.c
+ gcc -c $(CFLAGS) $< -o $@
+
+mkyaffsimage: $(MKYAFFSIMAGEOBJS)
+ gcc -o $@ $(MKYAFFSIMAGEOBJS)
+
+$(MKYAFFSOBJS): %.o: %.c
gcc -c $(CFLAGS) $< -o $@
-mkyaffs: $(OBJS)
- gcc -o $@ $(OBJS)
+mkyaffs: $(MKYAFFSOBJS)
+ gcc -o $@ $(MKYAFFSOBJS)
clean:
- rm -f $(OBJS) core
+ rm -f $(MKYAFFSOBJS) $(MKYAFFSIMAGEOBJS) core
diff --git a/utils/mkyaffs b/utils/mkyaffs
index 27e3a086d46a85f525a1695a96617c9ac95d9968..3bf419059c64fda043fd7026953e7d2535aca490 100755
GIT binary patch
literal 15115
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
Wf349{q~#Hr{coy>*EH^@i~k4NXcWEx
literal 6382
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
J#bqzT{|0
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "yaffs_guts.h"
+
+
+#define MAX_OBJECTS 10000
+
+// External functions for ECC on data
+void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
+int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+
+
+
+
+
+typedef struct
+{
+ dev_t dev;
+ ino_t ino;
+ int obj;
+} objItem;
+
+
+static objItem obj_list[MAX_OBJECTS];
+static int n_obj = 0;
+static int obj_id = YAFFS_NOBJECT_BUCKETS + 1;
+
+static int nObjects, nDirectories, nPages;
+
+static int outFile;
+
+static int error;
+
+
+int obj_compare(const void *a, const void * b)
+{
+ objItem *oa, *ob;
+
+ oa = (objItem *)a;
+ ob = (objItem *)b;
+
+ if(oa->dev < ob->dev) return -1;
+ if(oa->dev > ob->dev) return 1;
+ if(oa->ino < ob->ino) return -1;
+ if(oa->ino > ob->ino) return 1;
+
+ return 0;
+}
+
+
+void add_obj_to_list(dev_t dev, ino_t ino, int obj)
+{
+ if(n_obj < MAX_OBJECTS)
+ {
+ obj_list[n_obj].dev = dev;
+ obj_list[n_obj].ino = ino;
+ obj_list[n_obj].obj = obj;
+ n_obj++;
+ qsort(obj_list,n_obj,sizeof(objItem),obj_compare);
+
+ }
+ else
+ {
+ // oops! not enough space in the object array
+ fprintf(stderr,"Not enough space in object array\n");
+ exit(2);
+ }
+}
+
+
+int find_obj_in_list(dev_t dev, ino_t ino)
+{
+ objItem *i = NULL;
+ objItem test;
+
+ test.dev = dev;
+ test.ino = ino;
+
+ if(n_obj > 0)
+ {
+ i = bsearch(&test,obj_list,n_obj,sizeof(objItem),obj_compare);
+ }
+
+ if(i)
+ {
+ return i->obj;
+ }
+ return -1;
+}
+
+
+
+void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
+{
+ nand_calculate_ecc (data , spare->ecc1);
+ nand_calculate_ecc (&data[256] , spare->ecc2);
+}
+
+void yaffs_CalcTagsECC(yaffs_Tags *tags)
+{
+ // Todo don't do anything yet. Need to calculate ecc
+ unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
+ unsigned i,j;
+ unsigned ecc = 0;
+ unsigned bit = 0;
+
+ tags->ecc = 0;
+
+ for(i = 0; i < 8; i++)
+ {
+ for(j = 1; j &0x7f; j<<=1)
+ {
+ bit++;
+ if(b[i] & j)
+ {
+ ecc ^= bit;
+ }
+ }
+ }
+
+ tags->ecc = ecc;
+
+
+}
+static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr)
+{
+ yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
+
+ yaffs_CalcTagsECC(tagsPtr);
+
+ sparePtr->tagByte0 = tu->asBytes[0];
+ sparePtr->tagByte1 = tu->asBytes[1];
+ sparePtr->tagByte2 = tu->asBytes[2];
+ sparePtr->tagByte3 = tu->asBytes[3];
+ sparePtr->tagByte4 = tu->asBytes[4];
+ sparePtr->tagByte5 = tu->asBytes[5];
+ sparePtr->tagByte6 = tu->asBytes[6];
+ sparePtr->tagByte7 = tu->asBytes[7];
+}
+
+
+
+int write_chunk(__u8 *data, __u32 objId, __u32 chunkId, __u32 nBytes)
+{
+ yaffs_Tags t;
+ yaffs_Spare s;
+
+ error = write(outFile,data,512);
+ if(error < 0) return error;
+
+ memset(&t,0xff,sizeof (yaffs_Tags));
+
+ t.chunkId = chunkId;
+ t.serialNumber = 0;
+ t.byteCount = nBytes;
+ t.objectId = objId;
+
+ yaffs_CalcTagsECC(&t);
+ yaffs_LoadTagsIntoSpare(&s,&t);
+ yaffs_CalcECC(data,&s);
+
+ nPages++;
+
+ return write(outFile,&s,sizeof(yaffs_Spare));
+
+}
+
+int write_object_header(int objId, yaffs_ObjectType t, struct stat *s, int parent, const char *name, int equivalentObj, const char * alias)
+{
+ __u8 bytes[512];
+
+
+ yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bytes;
+
+ memset(bytes,0xff,512);
+
+ oh->type = t;
+
+ oh->parentObjectId = parent;
+
+ strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);
+
+ if(t != YAFFS_OBJECT_TYPE_HARDLINK)
+ {
+ oh->st_mode = s->st_mode;
+ oh->st_uid = s->st_uid;
+ oh->st_gid = s->st_uid;
+ oh->st_atime = s->st_atime;
+ oh->st_mtime = s->st_mtime;
+ oh->st_ctime = s->st_ctime;
+ oh->st_rdev = s->st_rdev;
+ }
+
+ if(t == YAFFS_OBJECT_TYPE_FILE)
+ {
+ oh->fileSize = s->st_size;
+ }
+
+ if(t == YAFFS_OBJECT_TYPE_HARDLINK)
+ {
+ oh->equivalentObjectId = equivalentObj;
+ }
+
+ if(t == YAFFS_OBJECT_TYPE_SYMLINK)
+ {
+ strncpy(oh->alias,alias,YAFFS_MAX_ALIAS_LENGTH);
+ }
+
+ return write_chunk(bytes,objId,0,0xffff);
+
+}
+
+
+int process_directory(int parent, const char *path)
+{
+
+ DIR *dir;
+ struct dirent *entry;
+
+ nDirectories++;
+
+ dir = opendir(path);
+
+ if(dir)
+ {
+ while((entry = readdir(dir)) != NULL)
+ {
+
+ /* Ignore . and .. */
+ if(strcmp(entry->d_name,".") &&
+ strcmp(entry->d_name,".."))
+ {
+ char full_name[500];
+ struct stat stats;
+ int equivalentObj;
+ int newObj;
+
+ sprintf(full_name,"%s/%s",path,entry->d_name);
+
+ lstat(full_name,&stats);
+
+ if(S_ISLNK(stats.st_mode) ||
+ S_ISREG(stats.st_mode) ||
+ S_ISDIR(stats.st_mode) ||
+ S_ISFIFO(stats.st_mode) ||
+ S_ISBLK(stats.st_mode) ||
+ S_ISCHR(stats.st_mode) ||
+ S_ISSOCK(stats.st_mode))
+ {
+
+ newObj = obj_id++;
+ nObjects++;
+
+ printf("Object %d, %s is a ",newObj,full_name);
+
+ /* We're going to create an object for it */
+ if((equivalentObj = find_obj_in_list(stats.st_dev, stats.st_ino)) > 0)
+ {
+ /* we need to make a hard link */
+ printf("hard link to object %d\n",equivalentObj);
+ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL);
+ }
+ else
+ {
+
+ add_obj_to_list(stats.st_dev,stats.st_ino,newObj);
+
+ if(S_ISLNK(stats.st_mode))
+ {
+
+ char symname[500];
+
+ memset(symname,0,500);
+
+ readlink(full_name,symname,499);
+
+ printf("symlink to \"%s\"\n",symname);
+ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname);
+
+ }
+ else if(S_ISREG(stats.st_mode))
+ {
+ printf("file, ");
+ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL);
+
+ if(error >= 0)
+ {
+ int h;
+ __u8 bytes[512];
+ int nBytes;
+ int chunk = 0;
+
+ h = open(full_name,O_RDONLY);
+ if(h >= 0)
+ {
+ memset(bytes,0xff,512);
+ while((nBytes = read(h,bytes,512)) > 0)
+ {
+ chunk++;
+ write_chunk(bytes,newObj,chunk,nBytes);
+ memset(bytes,0xff,512);
+ }
+ if(nBytes < 0)
+ error = nBytes;
+
+ printf("%d data chunks written\n",chunk);
+ }
+ else
+ {
+ perror("Error opening file");
+ }
+ close(h);
+
+ }
+
+ }
+ else if(S_ISSOCK(stats.st_mode))
+ {
+ printf("socket\n");
+ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL);
+ }
+ else if(S_ISFIFO(stats.st_mode))
+ {
+ printf("fifo\n");
+ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL);
+ }
+ else if(S_ISCHR(stats.st_mode))
+ {
+ printf("character device\n");
+ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL);
+ }
+ else if(S_ISBLK(stats.st_mode))
+ {
+ printf("block device\n");
+ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL);
+ }
+ else if(S_ISDIR(stats.st_mode))
+ {
+ printf("directory\n");
+ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL);
+ process_directory(1,full_name);
+ }
+ }
+ }
+ else
+ {
+ printf(" we don't handle this type\n");
+ }
+ }
+ }
+ }
+
+ return 0;
+
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct stat stats;
+
+ printf("mkyaffsimage: image building tool for YAFFS built "__DATE__"\n");
+
+ if(argc != 3)
+ {
+ printf("usage: mkyaffsimage dir image_file\n");
+ printf(" dir the directory tree to be converted\n");
+ printf(" image_file the ouput file to hold the image\n");
+ exit(1);
+ }
+
+ if(stat(argv[1],&stats) < 0)
+ {
+ printf("Could not stat %s\n",argv[1]);
+ exit(1);
+ }
+
+ if(!S_ISDIR(stats.st_mode))
+ {
+ printf(" %s is not a directory\n",argv[1]);
+ exit(1);
+ }
+
+ outFile = open(argv[2],O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE);
+
+
+ if(outFile < 0)
+ {
+ printf("Could not open output file %s\n",argv[2]);
+ exit(1);
+ }
+
+ printf("Processing directory %s into image file %s\n",argv[1],argv[2]);
+
+ error = process_directory(YAFFS_OBJECTID_ROOT,argv[1]);
+
+ close(outFile);
+
+ if(error < 0)
+ {
+ perror("operation incomplete");
+ exit(1);
+ }
+ else
+ {
+ printf("Operation complete.\n"
+ "%d objects in %d directories\n"
+ "%d NAND pages\n",nObjects, nDirectories, nPages);
+ }
+
+ close(outFile);
+
+ exit(0);
+}
+
diff --git a/yaffs_fileem.c b/yaffs_fileem.c
index a034610..dbbe5f8 100644
--- a/yaffs_fileem.c
+++ b/yaffs_fileem.c
@@ -26,7 +26,7 @@
#include
#include
-#define FILE_SIZE_IN_MEG 2
+#define FILE_SIZE_IN_MEG 32
#define BLOCK_SIZE (32 * 528)
#define BLOCKS_PER_MEG ((1024*1024)/(32 * 512))
@@ -37,6 +37,21 @@
static int h;
static __u8 ffChunk[528];
+static int eraseDisplayEnabled;
+
+static int markedBadBlocks[] = { 1, 4, -1};
+
+static int IsAMarkedBadBlock(int blk)
+{
+ int *m = markedBadBlocks;
+
+ while(*m >= 0)
+ {
+ if(*m == blk) return 1;
+ m++;
+ }
+ return 0;
+}
static void CheckInit(yaffs_Device *dev)
@@ -68,8 +83,18 @@ static void CheckInit(yaffs_Device *dev)
for(i = 0; i < FILE_SIZE_IN_BLOCKS; i++)
{
yaffs_FEEraseBlockInNAND(dev,i);
+
+ if(IsAMarkedBadBlock(i))
+ {
+ yaffs_Spare spare;
+ memset(&spare,0xff,sizeof(spare));
+ spare.blockStatus = 1;
+
+ yaffs_FEWriteChunkToNAND(dev, i * 32,NULL,&spare);
+ }
}
}
+ eraseDisplayEnabled = 1;
}
}
@@ -133,7 +158,10 @@ int yaffs_FEEraseBlockInNAND(yaffs_Device *dev,int blockInNAND)
CheckInit(dev);
- printf("Erasing block %d\n",blockInNAND);
+ if(eraseDisplayEnabled)
+ {
+ printf("Erasing block %d\n",blockInNAND);
+ }
lseek(h,blockInNAND * BLOCK_SIZE,SEEK_SET);
for(i = 0; i < 32; i++)
diff --git a/yaffs_fs.c b/yaffs_fs.c
index 6a2ddb3..1a9460e 100644
--- a/yaffs_fs.c
+++ b/yaffs_fs.c
@@ -238,7 +238,7 @@ static void yaffs_put_inode(struct inode *inode)
static int yaffs_readpage(struct file *f, struct page * pg)
{
- struct yaffs_Object *obj;
+ yaffs_Object *obj;
unsigned char *pg_buf;
int ret;
@@ -255,7 +255,7 @@ static int yaffs_readpage(struct file *f, struct page * pg)
pg_buf = kmap(pg);
/* FIXME: Can kmap fail? */
- ret = yaffs_ReadDataFromFile(obj,pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
+ ret = yaffs_ReadDataFromFile(obj, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
if(ret >= 0) ret = 0;
@@ -312,7 +312,7 @@ static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object *obj)
inode->i_gid = obj->st_gid;
inode->i_blksize = YAFFS_BYTES_PER_CHUNK;
inode->i_blocks = 0;
- inode->i_rdev = NODEV;
+ inode->i_rdev = obj->st_rdev;;
inode->i_atime = obj->st_atime;
inode->i_mtime = obj->st_mtime;
inode->i_ctime = obj->st_ctime;
@@ -324,8 +324,8 @@ static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object *obj)
switch (obj->st_mode & S_IFMT)
{
- default:
- // init_special_inode(inode, mode, dev);
+ default: // fifo, device or socket
+ init_special_inode(inode, obj->st_mode, obj->st_rdev);
break;
case S_IFREG: // file
inode->i_op = &yaffs_file_inode_operations;
@@ -526,13 +526,15 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int d
return -EPERM;
}
- T(("yaffs_mknod: making oject for %s, mode %x\n",
- dentry->d_name.name, mode));
+ T(("yaffs_mknod: making oject for %s, mode %x dev %x\n",
+ dentry->d_name.name, mode,dev));
switch (mode & S_IFMT)
{
default:
-
+ // Special (socket, fifo, device...)
+ T((KERN_DEBUG"yaffs_mknod: making special\n"));
+ obj = yaffs_MknodSpecial(parent,dentry->d_name.name,mode,current->uid, current->gid,dev);
break;
case S_IFREG: // file
T((KERN_DEBUG"yaffs_mknod: making file\n"));
diff --git a/yaffs_guts.c b/yaffs_guts.c
index 4c7e81a..caf9289 100644
--- a/yaffs_guts.c
+++ b/yaffs_guts.c
@@ -1,3298 +1,3540 @@
-/*
- * YAFFS: Yet another FFS. A NAND-flash specific file system.
- * yaffs_guts.c The main guts of YAFFS
- *
- * Copyright (C) 2002 Aleph One Ltd.
- * for Toby Churchill Ltd and Brightstar Engineering
- *
- * Created by Charles Manning
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
- //yaffs_guts.c
-
-#include "yportenv.h"
-
-#include "yaffsinterface.h"
-#include "yaffs_guts.h"
-
-
-#if 0
-#define T(x) printf x
-#else
-#define T(x)
-#endif
-
-// External functions for ECC on data
-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
-
-
-// countBits is a quick way of counting the number of bits in a byte.
-// ie. countBits[n] holds the number of 1 bits in a byte with the value n.
-
-static const char yaffs_countBits[256] =
-{
-0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
-1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
-1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
-1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
-3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
-4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
-};
-
-
-
-// Device info
-//static yaffs_Device *yaffs_device;
-//yaffs_Object *yaffs_rootDir;
-//yaffs_Object *yaffs_lostNFound;
-
-
-
-// Local prototypes
-static int yaffs_CheckObjectHashSanity(yaffs_Device *dev);
-static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr);
-static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);
-static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan);
-
-static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type);
-static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj);
-static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name);
-static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId);
-static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);
-static int yaffs_CheckStructures(void);
-
-loff_t yaffs_GetFileSize(yaffs_Object *obj);
-
-
-static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);
-
-#ifdef YAFFS_PARANOID
-static int yaffs_CheckFileSanity(yaffs_Object *in);
-#else
-#define yaffs_CheckFileSanity(in)
-#endif
-
-static int __inline__ yaffs_HashFunction(int n)
-{
- return (n % YAFFS_NOBJECT_BUCKETS);
-}
-
-
-yaffs_Object *yaffs_Root(yaffs_Device *dev)
-{
- return dev->rootDir;
-}
-
-yaffs_Object *yaffs_LostNFound(yaffs_Device *dev)
-{
- return dev->lostNFoundDir;
-}
-
-
-static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
-{
- return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);
-}
-
-int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
-{
- int retVal;
- __u8 calcEcc[3];
- yaffs_Spare localSpare;
-
- if(!spare && data)
- {
- // If we don't have a real spare, then we use a local one.
- // Need this for the calculation of the ecc
- spare = &localSpare;
- }
-
- retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
- if(data)
- {
- // Do ECC correction
- //Todo handle any errors
- nand_calculate_ecc(data,calcEcc);
- nand_correct_data (data,spare->ecc1, calcEcc);
- nand_calculate_ecc(&data[256],calcEcc);
- nand_correct_data (&data[256],spare->ecc2, calcEcc);
- }
- return retVal;
-}
-
-#ifdef YAFFS_PARANOID
-
-static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)
-{
- static int init = 0;
- static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];
- static __u8 data[YAFFS_BYTES_PER_CHUNK];
- static __u8 spare[16];
-
- int retVal;
-
- retVal = YAFFS_OK;
-
- dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare);
-
-
-
- if(!init)
- {
- memset(cmpbuf,0xff,YAFFS_BYTES_PER_CHUNK);
- init = 1;
- }
-
- if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) retVal = YAFFS_FAIL;
- if(memcmp(cmpbuf,spare,16)) retVal = YAFFS_FAIL;
-
- return retVal;
-
-}
-
-#endif
-
-
-int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
-{
- return dev->eraseBlockInNAND(dev,blockInNAND);
-}
-
-int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
-{
- return dev->initialiseNAND(dev);
-}
-
-static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_Spare *spare,int useReserve)
-{
- int chunk;
-
- int writeOk = 0;
-
- unsigned char rbData[YAFFS_BYTES_PER_CHUNK];
- yaffs_Spare rbSpare;
-
- do{
- chunk = yaffs_AllocateChunk(dev,useReserve);
-
- if(chunk >= 0)
- {
- writeOk = yaffs_WriteChunkToNAND(dev,chunk,data,spare);
- if(writeOk)
- {
- // Readback & verify
- // If verify fails, then delete this chunk and try again
- // To verify we compare everything except the block and
- // page status bytes.
- yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare);
-
- if(memcmp(data,rbData,YAFFS_BYTES_PER_CHUNK) != 0 ||
- spare->tagByte0 != rbSpare.tagByte0 ||
- spare->tagByte1 != rbSpare.tagByte1 ||
- spare->tagByte2 != rbSpare.tagByte2 ||
- spare->tagByte3 != rbSpare.tagByte3 ||
- spare->tagByte4 != rbSpare.tagByte4 ||
- spare->tagByte5 != rbSpare.tagByte5 ||
- spare->tagByte6 != rbSpare.tagByte6 ||
- spare->tagByte7 != rbSpare.tagByte7 ||
- spare->ecc1[0] != rbSpare.ecc1[0] ||
- spare->ecc1[1] != rbSpare.ecc1[1] ||
- spare->ecc1[2] != rbSpare.ecc1[2] ||
- spare->ecc2[0] != rbSpare.ecc2[0] ||
- spare->ecc2[1] != rbSpare.ecc2[1] ||
- spare->ecc2[2] != rbSpare.ecc2[2] )
- {
- // Didn't verify
- yaffs_DeleteChunk(dev,chunk);
- writeOk = 0;
- }
-
- }
- }
- } while(chunk >= 0 && ! writeOk);
-
- return chunk;
-}
-
-
-
-
-///////////////////////// Object management //////////////////
-// List of spare objects
-// The list is hooked together using the first pointer
-// in the object
-
-// static yaffs_Object *yaffs_freeObjects = NULL;
-
-// static int yaffs_nFreeObjects;
-
-// static yaffs_ObjectList *yaffs_allocatedObjectList = NULL;
-
-// static yaffs_ObjectBucket yaffs_objectBucket[YAFFS_NOBJECT_BUCKETS];
-
-
-static __u16 yaffs_CalcNameSum(const char *name)
-{
- __u16 sum = 0;
- __u16 i = 1;
-
- __u8 *bname = (__u8 *)name;
-
- while (*bname)
- {
- sum += (*bname) * i;
- i++;
- bname++;
- }
- return sum;
-}
-
-
-void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
-{
- nand_calculate_ecc (data , spare->ecc1);
- nand_calculate_ecc (&data[256] , spare->ecc2);
-}
-
-void yaffs_CalcTagsECC(yaffs_Tags *tags)
-{
- // Todo don't do anything yet. Need to calculate ecc
- unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
- unsigned i,j;
- unsigned ecc = 0;
- unsigned bit = 0;
-
- tags->ecc = 0;
-
- for(i = 0; i < 8; i++)
- {
- for(j = 1; j &0x7f; j<<=1)
- {
- bit++;
- if(b[i] & j)
- {
- ecc ^= bit;
- }
- }
- }
-
- tags->ecc = ecc;
-
-
-}
-
-void yaffs_CheckECCOnTags(yaffs_Tags *tags)
-{
- unsigned ecc = tags->ecc;
-
- yaffs_CalcTagsECC(tags);
-
- ecc ^= tags->ecc;
-
- if(ecc)
- {
- // Needs fixing
- unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
-
- ecc--;
-
- b[ecc / 8] ^= (1 << (ecc & 7));
-
- // Now recvalc the ecc
- yaffs_CalcTagsECC(tags);
- }
-}
-
-
-///////////////////////// TNODES ///////////////////////
-
-// List of spare tnodes
-// The list is hooked together using the first pointer
-// in the tnode.
-
-//static yaffs_Tnode *yaffs_freeTnodes = NULL;
-
-// static int yaffs_nFreeTnodes;
-
-//static yaffs_TnodeList *yaffs_allocatedTnodeList = NULL;
-
-
-
-// yaffs_CreateTnodes creates a bunch more tnodes and
-// adds them to the tnode free list.
-// Don't use this function directly
-
-static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes)
-{
- int i;
- yaffs_Tnode *newTnodes;
- yaffs_TnodeList *tnl;
-
- if(nTnodes < 1) return YAFFS_OK;
-
- // make these things
-
- newTnodes = YMALLOC(nTnodes * sizeof(yaffs_Tnode));
-
- if (!newTnodes)
- {
- YALERT("Could not malloc tnodes");
- return YAFFS_FAIL;
- }
-
- // Hook them into the free list
- for(i = 0; i < nTnodes - 1; i++)
- {
- newTnodes[i].internal[0] = &newTnodes[i+1];
- }
-
- newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
- dev->freeTnodes = newTnodes;
- dev->nFreeTnodes+= nTnodes;
- dev->nTnodesCreated += nTnodes;
-
- // Now add this bunch of tnodes to a list for freeing up.
-
- tnl = YMALLOC(sizeof(yaffs_TnodeList));
- if(!tnl)
- {
- YALERT("Could not add tnodes to management list");
- }
- else
- {
- tnl->tnodes = newTnodes;
- tnl->next = dev->allocatedTnodeList;
- dev->allocatedTnodeList = tnl;
- }
-
-
- YINFO("Tnodes created");
-
-
- return YAFFS_OK;
-}
-
-
-// GetTnode gets us a clean tnode. Tries to make allocate more if we run out
-static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
-{
- yaffs_Tnode *tn = NULL;
-
- // If there are none left make more
- if(!dev->freeTnodes)
- {
- yaffs_CreateTnodes(dev,YAFFS_ALLOCATION_NTNODES);
- }
-
- if(dev->freeTnodes)
- {
- tn = dev->freeTnodes;
- dev->freeTnodes = dev->freeTnodes->internal[0];
- dev->nFreeTnodes--;
- // zero out
- memset(tn,0,sizeof(yaffs_Tnode));
- }
-
-
- return tn;
-}
-
-
-// FreeTnode frees up a tnode and puts it back on the free list
-static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn)
-{
- tn->internal[0] = dev->freeTnodes;
- dev->freeTnodes = tn;
- dev->nFreeTnodes++;
-}
-
-
-static void yaffs_DeinitialiseTnodes(yaffs_Device*dev)
-{
- // Free the list of allocated tnodes
-
- while(dev->allocatedTnodeList)
- {
- YFREE(dev->allocatedTnodeList->tnodes);
- dev->allocatedTnodeList = dev->allocatedTnodeList->next;
- }
-
- dev->freeTnodes = NULL;
- dev->nFreeTnodes = 0;
-}
-
-static void yaffs_InitialiseTnodes(yaffs_Device*dev)
-{
- dev->allocatedTnodeList = NULL;
- dev->freeTnodes = NULL;
- dev->nFreeTnodes = 0;
- dev->nTnodesCreated = 0;
-
-}
-
-void yaffs_TnodeTest(yaffs_Device *dev)
-{
-
- int i;
- int j;
- yaffs_Tnode *tn[1000];
-
- YINFO("Testing TNodes");
-
- for(j = 0; j < 50; j++)
- {
- for(i = 0; i < 1000; i++)
- {
- tn[i] = yaffs_GetTnode(dev);
- if(!tn[i])
- {
- YALERT("Getting tnode failed");
- }
- }
- for(i = 0; i < 1000; i+=3)
- {
- yaffs_FreeTnode(dev,tn[i]);
- tn[i] = NULL;
- }
-
- }
-}
-
-////////////////// END OF TNODE MANIPULATION ///////////////////////////
-
-/////////////// Functions to manipulate the look-up tree (made up of tnodes)
-// The look up tree is represented by the top tnode and the number of topLevel
-// in the tree. 0 means only the level 0 tnode is in the tree.
-
-
-// FindLevel0Tnode finds the level 0 tnode, if one exists.
-// Used when reading.....
-static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure *fStruct, __u32 chunkId)
-{
-
- yaffs_Tnode *tn = fStruct->top;
- __u32 i;
- int requiredTallness;
- int level = fStruct->topLevel;
-
- // Check sane level and chunk Id
- if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
- {
- char str[50];
- sprintf(str,"Bad level %d",level);
- YALERT(str);
- return NULL;
- }
- if(chunkId > YAFFS_MAX_CHUNK_ID)
- {
- char str[50];
- sprintf(str,"Bad chunkId %d",chunkId);
- YALERT(str);
- return NULL;
- }
-
- // First check we're tall enough (ie enough topLevel)
-
- i = chunkId >> (dev->chunkGroupBits + YAFFS_TNODES_LEVEL0_BITS);
- requiredTallness = 0;
- while(i)
- {
- i >>= YAFFS_TNODES_INTERNAL_BITS;
- requiredTallness++;
- }
-
-
- if(requiredTallness > fStruct->topLevel)
- {
- // Not tall enough, so we can't find it, return NULL.
- return NULL;
- }
-
-
- // Traverse down to level 0
- while (level > 0 && tn)
- {
- tn = tn->internal[(chunkId >>(dev->chunkGroupBits + YAFFS_TNODES_LEVEL0_BITS + (level-1) * YAFFS_TNODES_INTERNAL_BITS)) &
- YAFFS_TNODES_INTERNAL_MASK];
- level--;
-
- }
-
- return tn;
-}
-
-// AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.
-// This happens in two steps:
-// 1. If the tree isn't tall enough, then make it taller.
-// 2. Scan down the tree towards the level 0 tnode adding tnodes if required.
-//
-// Used when modifying the tree.
-//
-static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId)
-{
-
- yaffs_Tnode *tn;
-
- int requiredTallness;
-
- __u32 i;
- __u32 l;
-
-
- T(("AddOrFind topLevel=%d, chunk=%d",fStruct->topLevel,chunkId));
-
- // Check sane level and page Id
- if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL)
- {
- char str[50];
- sprintf(str,"Bad level %d",fStruct->topLevel);
- YALERT(str);
- return NULL;
- }
-
- if(chunkId > YAFFS_MAX_CHUNK_ID)
- {
- char str[50];
- sprintf(str,"Bad chunkId %d",chunkId);
- YALERT(str);
- return NULL;
- }
-
- // First check we're tall enough (ie enough topLevel)
-
- i = chunkId >> (dev->chunkGroupBits + YAFFS_TNODES_LEVEL0_BITS);
- requiredTallness = 0;
- while(i)
- {
- i >>= YAFFS_TNODES_INTERNAL_BITS;
- requiredTallness++;
- }
-
- T((" required=%d",requiredTallness));
-
-
- if(requiredTallness > fStruct->topLevel)
- {
- // Not tall enough,gotta make the tree taller
- for(i = fStruct->topLevel; i < requiredTallness; i++)
- {
- T((" add new top"));
-
- tn = yaffs_GetTnode(dev);
-
- if(tn)
- {
- tn->internal[0] = fStruct->top;
- fStruct->top = tn;
- }
- else
- {
- YALERT("No more tnodes");
- }
- }
-
- fStruct->topLevel = requiredTallness;
- }
-
-
- // Traverse down to level 0, adding anything we need
-
- l = fStruct->topLevel;
- tn = fStruct->top;
- while (l > 0 && tn)
- {
- i = (chunkId >> (dev->chunkGroupBits +YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) &
- YAFFS_TNODES_INTERNAL_MASK;
-
- T((" [%d:%d]",l,i));
-
- if(!tn->internal[i])
- {
- T((" added"));
-
- tn->internal[i] = yaffs_GetTnode(dev);
- }
-
- tn = tn->internal[i];
- l--;
-
- }
-
- T(("\n"));
-
- return tn;
-}
-
-
-// Pruning removes any part of the file structure tree that is beyond the
-// bounds of the file.
-// A file should only get pruned when its size is reduced.
-//
-// Before pruning, the chunks must be pulled from the tree and the
-// level 0 tnode entries must be zeroed out.
-// Could also use this for file deletion, but that's probably better handled
-// by a special case.
-
-// yaffs_PruneWorker should only be called by yaffs_PruneFileStructure()
-
-static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, __u32 level, int del0)
-{
- int i;
- int hasData;
-
- if(tn)
- {
- hasData = 0;
-
- for(i = 0; i < YAFFS_NTNODES_INTERNAL; i++)
- {
- if(tn->internal[i] && level > 0)
- {
- tn->internal[i] = yaffs_PruneWorker(dev,tn->internal[i],level - 1, ( i == 0) ? del0 : 1);
- }
-
- if(tn->internal[i])
- {
- hasData++;
- }
- }
-
- if(hasData == 0 && del0)
- {
- // Free and return NULL
-
- yaffs_FreeTnode(dev,tn);
- tn = NULL;
- }
-
- }
-
- return tn;
-
-}
-
-static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStruct)
-{
- int i;
- int hasData;
- int done = 0;
- yaffs_Tnode *tn;
-
- if(fStruct->topLevel > 0)
- {
- fStruct->top = yaffs_PruneWorker(dev,fStruct->top, fStruct->topLevel,0);
-
- // Now we have a tree with all the non-zero branches NULL but the height
- // is the same as it was.
- // Let's see if we can trim internal tnodes to shorten the tree.
- // We can do this if only the 0th element in the tnode is in use
- // (ie all the non-zero are NULL)
-
- while(fStruct->topLevel && !done)
- {
- tn = fStruct->top;
-
- hasData = 0;
- for(i = 1; i internal[i])
- {
- hasData++;
- }
- }
-
- if(!hasData)
- {
- fStruct->top = tn->internal[0];
- fStruct->topLevel--;
- yaffs_FreeTnode(dev,tn);
- }
- else
- {
- done = 1;
- }
- }
- }
-
- return YAFFS_OK;
-}
-
-
-
-
-/////////////////////// End of File Structure functions. /////////////////
-
-// yaffs_CreateFreeObjects creates a bunch more objects and
-// adds them to the object free list.
-static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)
-{
- int i;
- yaffs_Object *newObjects;
- yaffs_ObjectList *list;
-
- if(nObjects < 1) return YAFFS_OK;
-
- // make these things
-
- newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));
-
- if (!newObjects)
- {
- YALERT("Could not allocate more objects");
- return YAFFS_FAIL;
- }
-
- // Hook them into the free list
- for(i = 0; i < nObjects - 1; i++)
- {
- (yaffs_Object *)newObjects[i].siblings.next = &newObjects[i+1];
- }
-
- newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
- dev->freeObjects = newObjects;
- dev->nFreeObjects+= nObjects;
- dev->nObjectsCreated+= nObjects;
-
- // Now add this bunch of Objects to a list for freeing up.
-
- list = YMALLOC(sizeof(yaffs_ObjectList));
- if(!list)
- {
- YALERT("Could not add Objects to management list");
- }
- else
- {
- list->objects = newObjects;
- list->next = dev->allocatedObjectList;
- dev->allocatedObjectList = list;
- }
-
-
- YINFO("Objects created");
-
-
- return YAFFS_OK;
-}
-
-
-// AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out
-static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)
-{
- yaffs_Object *tn = NULL;
-
- // If there are none left make more
- if(!dev->freeObjects)
- {
- yaffs_CreateFreeObjects(dev,YAFFS_ALLOCATION_NOBJECTS);
- }
-
- if(dev->freeObjects)
- {
- tn = dev->freeObjects;
- dev->freeObjects = (yaffs_Object *)(dev->freeObjects->siblings.next);
- dev->nFreeObjects--;
-
- // Now sweeten it up...
-
- memset(tn,0,sizeof(yaffs_Object));
- tn->chunkId = -1;
- tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
- INIT_LIST_HEAD(&(tn->hardLinks));
- INIT_LIST_HEAD(&(tn->hashLink));
- INIT_LIST_HEAD(&tn->siblings);
-
- // Add it to the lost and found directory.
- // NB Can't put root or lostNFound in lostNFound so
- // check if lostNFound exists first
- if(dev->lostNFoundDir)
- {
- yaffs_AddObjectToDirectory(dev->lostNFoundDir,tn);
- }
- }
-
-
- return tn;
-}
-
-static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u32 mode)
-{
-
- yaffs_Object *obj = yaffs_CreateNewObject(dev,number,YAFFS_OBJECT_TYPE_DIRECTORY);
- if(obj)
- {
- obj->fake = 1; // it is fake so it has no NAND presence...
- obj->renameAllowed= 0; // ... and we're not allowed to rename it...
- obj->unlinkAllowed= 0; // ... or unlink it
- obj->st_mode = mode;
- obj->myDev = dev;
- obj->chunkId = 0; // Not a valid chunk.
- }
-
- return obj;
-
-}
-
-
-static void yaffs_UnhashObject(yaffs_Object *tn)
-{
- int bucket;
- yaffs_Device *dev = tn->myDev;
-
-
- // If it is still linked into the bucket list, free from the list
- if(!list_empty(&tn->hashLink))
- {
- list_del_init(&tn->hashLink);
- bucket = yaffs_HashFunction(tn->objectId);
- dev->objectBucket[bucket].count--;
- }
-
-}
-
-
-// FreeObject frees up a Object and puts it back on the free list
-static void yaffs_FreeObject(yaffs_Object *tn)
-{
-
- yaffs_Device *dev = tn->myDev;
-
- yaffs_UnhashObject(tn);
-
- // Link into the free list.
- (yaffs_Object *)(tn->siblings.next) = dev->freeObjects;
- dev->freeObjects = tn;
- dev->nFreeObjects++;
-}
-
-
-
-
-static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
-{
- // Free the list of allocated Objects
-
- while( dev->allocatedObjectList)
- {
- YFREE(dev->allocatedObjectList->objects);
- dev->allocatedObjectList = dev->allocatedObjectList->next;
- }
-
- dev->freeObjects = NULL;
- dev->nFreeObjects = 0;
-}
-
-static void yaffs_InitialiseObjects(yaffs_Device *dev)
-{
- int i;
-
- dev->allocatedObjectList = NULL;
- dev->freeObjects = NULL;
- dev->nFreeObjects = 0;
-
- for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++)
- {
- INIT_LIST_HEAD(&dev->objectBucket[i].list);
- dev->objectBucket[i].count = 0;
- }
-
-}
-
-
-
-
-
-
-int yaffs_FindNiceObjectBucket(yaffs_Device *dev)
-{
- static int x = 0;
- int i;
- int l = 999;
- int lowest = 999999;
-
-
- // First let's see if we can find one that's empty.
-
- for(i = 0; i < 10 && lowest > 0; i++)
- {
- x++;
- x %= YAFFS_NOBJECT_BUCKETS;
- if(dev->objectBucket[x].count < lowest)
- {
- lowest = dev->objectBucket[x].count;
- l = x;
- }
-
- }
-
- // If we didn't find an empty list, then try
- // looking a bit further for a short one
-
- for(i = 0; i < 10 && lowest > 3; i++)
- {
- x++;
- x %= YAFFS_NOBJECT_BUCKETS;
- if(dev->objectBucket[x].count < lowest)
- {
- lowest = dev->objectBucket[x].count;
- l = x;
- }
-
- }
-
- return l;
-}
-
-static int yaffs_CreateNewObjectNumber(yaffs_Device *dev)
-{
- int bucket = yaffs_FindNiceObjectBucket(dev);
-
- // Now find an object value that has not already been taken
- // by scanning the list.
-
- int found = 0;
- struct list_head *i;
-
- int n = bucket;
-
- //yaffs_CheckObjectHashSanity();
-
- while(!found)
- {
- found = 1;
- n += YAFFS_NOBJECT_BUCKETS;
- if(1 ||dev->objectBucket[bucket].count > 0)
- {
- list_for_each(i,&dev->objectBucket[bucket].list)
- {
- // If there is already one in the list
- if(list_entry(i, yaffs_Object,hashLink)->objectId == n)
- {
- found = 0;
- }
- }
- }
- }
-
- //T(("bucket %d count %d inode %d\n",bucket,yaffs_objectBucket[bucket].count,n);
-
- return n;
-}
-
-void yaffs_HashObject(yaffs_Object *in)
-{
- int bucket = yaffs_HashFunction(in->objectId);
- yaffs_Device *dev = in->myDev;
-
- if(!list_empty(&in->hashLink))
- {
- YINFO("!!!");
- }
-
-
- list_add(&in->hashLink,&dev->objectBucket[bucket].list);
- dev->objectBucket[bucket].count++;
-
-}
-
-yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number)
-{
- int bucket = yaffs_HashFunction(number);
- struct list_head *i;
- yaffs_Object *in;
-
- list_for_each(i,&dev->objectBucket[bucket].list)
- {
- // Lookm if it is in the list
- in = list_entry(i, yaffs_Object,hashLink);
- if(in->objectId == number)
- {
- return in;
- }
- }
-
- return NULL;
-}
-
-
-
-yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type)
-{
-
- yaffs_Object *theObject;
-
- if(number < 0)
- {
- number = yaffs_CreateNewObjectNumber(dev);
- }
-
- theObject = yaffs_AllocateEmptyObject(dev);
-
- if(theObject)
- {
- theObject->fake = 0;
- theObject->renameAllowed = 1;
- theObject->unlinkAllowed = 1;
- theObject->objectId = number;
- theObject->myDev = dev;
- yaffs_HashObject(theObject);
- theObject->variantType = type;
- theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME;
-
- switch(type)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- theObject->variant.fileVariant.fileSize = 0;
- theObject->variant.fileVariant.topLevel = 0;
- theObject->variant.fileVariant.top = yaffs_GetTnode(dev);
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- INIT_LIST_HEAD(&theObject->variant.directoryVariant.children);
- break;
- case YAFFS_OBJECT_TYPE_SYMLINK:
- // No action required
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- // No action required
- break;
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- // todo this should not happen
- }
- }
-
- return theObject;
-}
-
-yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type)
-{
- yaffs_Object *theObject = NULL;
-
- if(number >= 0)
- {
- theObject = yaffs_FindObjectByNumber(dev,number);
- }
-
- if(!theObject)
- {
- theObject = yaffs_CreateNewObject(dev,number,type);
- }
-
- return theObject;
-
-}
-
-char *yaffs_CloneString(const char *str)
-{
- char *newStr = NULL;
-
- if(str && *str)
- {
- newStr = YMALLOC(strlen(str) + 1);
- strcpy(newStr,str);
- }
-
- return newStr;
-
-}
-
-//
-// Mknod (create) a new object.
-// equivalentObject only has meaning for a hard link;
-// aliasString only has meaning for a sumlink.
-yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,
- yaffs_Object *parent,
- const char *name,
- __u32 mode,
- __u32 uid,
- __u32 gid,
- yaffs_Object *equivalentObject,
- const char *aliasString)
-{
- yaffs_Object *in;
-
- yaffs_Device *dev = parent->myDev;
-
- // Check if the entry exists. If it does then fail the call since we don't want a dup.
- if(yaffs_FindObjectByName(parent,name))
- {
- return NULL;
- }
-
- in = yaffs_CreateNewObject(dev,-1,type);
-
- if(in)
- {
- in->chunkId = -1;
- in->valid = 1;
- in->variantType = type;
-
- in->st_mode = mode;
- in->st_uid = uid;
- in->st_gid = gid;
- in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME;
-
- in->sum = yaffs_CalcNameSum(name);
- in->dirty = 1;
-
- yaffs_AddObjectToDirectory(parent,in);
-
- in->myDev = parent->myDev;
-
-
- switch(type)
- {
- case YAFFS_OBJECT_TYPE_SYMLINK:
- in->variant.symLinkVariant.alias = yaffs_CloneString(aliasString);
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- in->variant.hardLinkVariant.equivalentObject = equivalentObject;
- in->variant.hardLinkVariant.equivalentObjectId = equivalentObject->objectId;
- list_add(&in->hardLinks,&equivalentObject->hardLinks);
- break;
- case YAFFS_OBJECT_TYPE_FILE: // do nothing
- case YAFFS_OBJECT_TYPE_DIRECTORY: // do nothing
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- }
-
- yaffs_UpdateObjectHeader(in,name);
-
- }
-
- return in;
-}
-
-yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)
-{
- return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE,parent,name,mode,uid,gid,NULL,NULL);
-}
-
-yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)
-{
- return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL);
-}
-
-yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,const char *alias)
-{
- return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK,parent,name,mode,uid,gid,NULL,alias);
-}
-
-// NB yaffs_Link returns the object id of the equivalent object.
-yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject)
-{
- // Get the real object in case we were fed a hard link as an equivalent object
- equivalentObject = yaffs_GetEquivalentObject(equivalentObject);
-
- if(yaffs_MknodObject(YAFFS_OBJECT_TYPE_HARDLINK,parent,name,0,0,0,equivalentObject,NULL))
- {
- return equivalentObject;
- }
- else
- {
- return NULL;
- }
-
-}
-
-
-static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName)
-{
- //yaffs_Device *dev = obj->myDev;
-
- if(newDir == NULL)
- {
- newDir = obj->parent; // use the old directory
- }
-
- // Only proceed if the new name does not exist and
- // if we're putting it into a directory.
- if(!yaffs_FindObjectByName(newDir,newName) &&
- newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
- {
- obj->sum = yaffs_CalcNameSum(newName);
- obj->dirty = 1;
- yaffs_AddObjectToDirectory(newDir,obj);
-
- if(yaffs_UpdateObjectHeader(obj,newName) >= 0)
- {
- return YAFFS_OK;
- }
- }
-
- return YAFFS_FAIL;
-}
-
-int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName)
-{
- yaffs_Object *obj;
-
- obj = yaffs_FindObjectByName(oldDir,oldName);
- if(obj && obj->renameAllowed)
- {
- return yaffs_ChangeObjectName(obj,newDir,newName);
- }
- return YAFFS_FAIL;
-}
-
-
-
-static int yaffs_CheckObjectHashSanity(yaffs_Device *dev)
-{
- // Scan the buckets and check that the lists
- // have as many members as the count says there are
- int bucket;
- int countEm;
- struct list_head *j;
- int ok = YAFFS_OK;
-
- for(bucket = 0; bucket < YAFFS_NOBJECT_BUCKETS; bucket++)
- {
- countEm = 0;
-
- list_for_each(j,&dev->objectBucket[bucket].list)
- {
- countEm++;
- }
-
- if(countEm != dev->objectBucket[bucket].count)
- {
- YALERT("Inode hash inconsistency");
- ok = YAFFS_FAIL;
- }
- }
-
- return ok;
-}
-
-void yaffs_ObjectTest(yaffs_Device *dev)
-{
- yaffs_Object *in[1000];
- int inNo[1000];
- yaffs_Object *inold[1000];
- int i;
- int j;
-
- memset(in,0,1000*sizeof(yaffs_Object *));
- memset(inold,0,1000*sizeof(yaffs_Object *));
-
- yaffs_CheckObjectHashSanity(dev);
-
- for(j = 0; j < 10; j++)
- {
- T(("%d\n",j));
-
- for(i = 0; i < 1000; i++)
- {
- in[i] = yaffs_CreateNewObject(dev,-1,YAFFS_OBJECT_TYPE_FILE);
- if(!in[i])
- {
- YINFO("No more inodes");
- }
- else
- {
- inNo[i] = in[i]->objectId;
- }
- }
-
- for(i = 0; i < 1000; i++)
- {
- if(yaffs_FindObjectByNumber(dev,inNo[i]) != in[i])
- {
- T(("Differnce in look up test\n"));
- }
- else
- {
- // T(("Look up ok\n"));
- }
- }
-
- yaffs_CheckObjectHashSanity(dev);
-
- for(i = 0; i < 1000; i+=3)
- {
- yaffs_FreeObject(in[i]);
- in[i] = NULL;
- }
-
-
- yaffs_CheckObjectHashSanity(dev);
- }
-
-}
-
-
-
-/////////////////////////// Block Management and Page Allocation ///////////////////
-
-
-static void yaffs_InitialiseBlocks(yaffs_Device *dev)
-{
- dev->blockInfo = YMALLOC(dev->nBlocks * sizeof(yaffs_BlockInfo));
- memset(dev->blockInfo,0,dev->nBlocks * sizeof(yaffs_BlockInfo));
- dev->allocationBlock = -1; // force it to get a new one
-}
-
-static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
-{
- YFREE(dev->blockInfo);
-}
-
-// FindDiretiestBlock is used to select the dirtiest block (or close enough)
-// for garbage collection.
-
-static int yaffs_FindDirtiestBlock(yaffs_Device *dev)
-{
-
- int b = dev->currentDirtyChecker;
-
- int i;
- int dirtiest = -1;
- int pagesInUse = 100; // silly big number
-
- for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)
- {
- b++;
- if (b > dev->endBlock)
- {
- b = dev->startBlock;
- }
-
- if(dev->blockInfo[b].blockState == YAFFS_BLOCK_STATE_FULL &&
- (dev->blockInfo)[b].pagesInUse < pagesInUse)
- {
- dirtiest = b;
- pagesInUse = (dev->blockInfo)[b].pagesInUse;
- }
- }
-
- dev->currentDirtyChecker = b;
-
- return dirtiest;
-}
-
-
-static int yaffs_FindBlockForAllocation(yaffs_Device *dev,int useReserve)
-{
- int i;
-
- if(useReserve && dev->nErasedBlocks < 1)
- {
- // Hoosterman we've got a problem.
- // Can't get space to gc
- return -1;
- }
- else if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)
- {
- // We are not in GC, so we hold some in reserve so we can get
- // a gc done.
- }
-
- // Find an empty block.
-
- for(i = dev->startBlock; i <= dev->endBlock; i++)
- {
-
- if(dev->blockInfo[i].blockState == YAFFS_BLOCK_STATE_EMPTY)
- {
- dev->blockInfo[i].blockState = YAFFS_BLOCK_STATE_ALLOCATING;
- dev->nErasedBlocks--;
- if(dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)
- {
- dev->garbageCollectionRequired = 1;
- }
-
- return i;
- }
- }
-
- return -1;
-}
-
-
-static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)
-{
- yaffs_BlockInfo *bi = &dev->blockInfo[blockNo];
-
- // Mark as dirty, erase it and mark as clean.
- bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
- yaffs_EraseBlockInNAND(dev,blockNo);
- bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
- dev->nErasedBlocks++;
- bi->pagesInUse = 0;
- bi->pageBits = 0;
-
- T(("Erased block %d\n",blockNo));
-}
-
-
-static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
-{
- int retVal;
-
- if(dev->allocationBlock < 0)
- {
- // Get next block to allocate off
- dev->allocationBlock = yaffs_FindBlockForAllocation(dev,useReserve);
- dev->allocationPage = 0;
- }
-
- // Next page please....
- if(dev->allocationBlock >= 0)
- {
- retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) +
- dev->allocationPage;
- dev->blockInfo[dev->allocationBlock].pagesInUse++;
- dev->blockInfo[dev->allocationBlock].pageBits |=
- (1 << (dev->allocationPage));
-
- dev->allocationPage++;
-
- dev->nFreeChunks--;
-
- // If the block is full set the state to full
- if(dev->allocationPage >= YAFFS_CHUNKS_PER_BLOCK)
- {
- dev->blockInfo[dev->allocationBlock].blockState = YAFFS_BLOCK_STATE_FULL;
- dev->allocationBlock = -1;
- }
-
-#ifdef YAFFS_PARANOID
- if(yaffs_CheckChunkErased(dev,retVal) == YAFFS_FAIL)
- {
- T(("..................Trying to allocate non-erased page %d\n",retVal));
- }
-#endif
- return retVal;
-
- }
- T(("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!\n"));
-
- return -1;
-}
-
-
-int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
-{
- int oldChunk;
- int newChunk;
- __u32 mask;
-
-
- yaffs_Spare spare;
- yaffs_Tags tags;
- __u8 buffer[YAFFS_BYTES_PER_CHUNK];
-
- yaffs_BlockInfo *bi = &dev->blockInfo[block];
-
- yaffs_Object *object;
-
- T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));
-
- for(mask = 1,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK;
- mask && bi->pageBits;
- mask <<= 1, oldChunk++ )
- {
- if(bi->pageBits & mask)
- {
-
- // This page is in use and needs to be copied off
-
- T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));
-
- yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare);
-
- yaffs_GetTagsFromSpare(&spare,&tags);
- tags.serialNumber++;
- yaffs_LoadTagsIntoSpare(&spare,&tags);
-
-#if 0
- newChunk = yaffs_AllocatePage(dev,1);
- if(newChunk < 0)
- {
- return YAFFS_FAIL;
- }
-
- yaffs_WriteChunkToNAND(dev,newChunk, buffer, &spare);
-
-#else
- newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);
-#endif
- if(newChunk < 0)
- {
- return YAFFS_FAIL;
- }
-
- object = yaffs_FindObjectByNumber(dev,tags.objectId);
-
- // Ok, now fix up the Tnodes etc.
-
- if(tags.chunkId == 0)
- {
- // It's a header
- object->chunkId = newChunk;
- }
- else
- {
- // It's a data chunk
- yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
-
- }
-
- yaffs_DeleteChunk(dev,oldChunk);
-
- }
- }
-
- return YAFFS_OK;
-}
-
-int yaffs_CheckGarbageCollection(yaffs_Device *dev)
-{
- int block;
-
- if(dev->garbageCollectionRequired)
- {
- dev->garbageCollectionRequired = 0;
- block = yaffs_FindDirtiestBlock(dev);
- if(block >= 0)
- {
- return yaffs_GarbageCollectBlock(dev,block);
- }
- else
- {
- return YAFFS_FAIL;
- }
- }
-
- return YAFFS_OK;
-}
-
-
-//////////////////////////// TAGS ///////////////////////////////////////
-
-static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr)
-{
- yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
-
- yaffs_CalcTagsECC(tagsPtr);
-
- sparePtr->tagByte0 = tu->asBytes[0];
- sparePtr->tagByte1 = tu->asBytes[1];
- sparePtr->tagByte2 = tu->asBytes[2];
- sparePtr->tagByte3 = tu->asBytes[3];
- sparePtr->tagByte4 = tu->asBytes[4];
- sparePtr->tagByte5 = tu->asBytes[5];
- sparePtr->tagByte6 = tu->asBytes[6];
- sparePtr->tagByte7 = tu->asBytes[7];
-}
-
-static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)
-{
- yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
-
- tu->asBytes[0]= sparePtr->tagByte0;
- tu->asBytes[1]= sparePtr->tagByte1;
- tu->asBytes[2]= sparePtr->tagByte2;
- tu->asBytes[3]= sparePtr->tagByte3;
- tu->asBytes[4]= sparePtr->tagByte4;
- tu->asBytes[5]= sparePtr->tagByte5;
- tu->asBytes[6]= sparePtr->tagByte6;
- tu->asBytes[7]= sparePtr->tagByte7;
-
- yaffs_CheckECCOnTags(tagsPtr);
-}
-
-static void yaffs_SpareInitialise(yaffs_Spare *spare)
-{
- memset(spare,0xFF,sizeof(yaffs_Spare));
-}
-
-static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags)
-{
- if(tags)
- {
- yaffs_Spare spare;
- if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare) == YAFFS_OK)
- {
- yaffs_GetTagsFromSpare(&spare,tags);
- return YAFFS_OK;
- }
- else
- {
- return YAFFS_FAIL;
- }
- }
-
- return YAFFS_OK;
-}
-
-#if 0
-static int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_Tags *tags)
-{
- // NB There must be tags, data is optional
- // If there is data, then an ECC is calculated on it.
-
- yaffs_Spare spare;
-
- if(!tags)
- {
- return YAFFS_FAIL;
- }
-
- yaffs_SpareInitialise(&spare);
-
-
- if(buffer)
- {
- yaffs_CalcECC(buffer,&spare);
- }
-
- yaffs_LoadTagsIntoSpare(&spare,tags);
-
- return yaffs_WriteChunkToNAND(dev,chunkInNAND,buffer,&spare);
-
-}
-#endif
-
-
-static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_Tags *tags, int useReserve)
-{
- // NB There must be tags, data is optional
- // If there is data, then an ECC is calculated on it.
-
- yaffs_Spare spare;
-
- if(!tags)
- {
- return YAFFS_FAIL;
- }
-
- yaffs_SpareInitialise(&spare);
-
-
- if(buffer)
- {
- yaffs_CalcECC(buffer,&spare);
- }
-
- yaffs_LoadTagsIntoSpare(&spare,tags);
-
- return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve);
-
-}
-
-
-
-
-int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
-{
- //Get the Tnode, then get the level 0 offset chunk offset
- yaffs_Tnode *tn;
- int theChunk = -1;
- yaffs_Tags localTags;
- int i;
- int found = 0;
- yaffs_Device *dev = in->myDev;
-
-
- if(!tags)
- {
- // Passed a NULL, so use our own tags space
- tags = &localTags;
- }
-
- tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
-
- if(tn)
- {
- theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
-
- // Now we need to do the shifting etc and search for it
- for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
- {
- yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags);
- if(tags->chunkId == chunkInInode &&
- tags->objectId == in->objectId)
- {
- // found it;
- found = 1;
- }
- else
- {
- theChunk++;
- }
- }
- }
- return found ? theChunk : -1;
-}
-
-int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
-{
- //Get the Tnode, then get the level 0 offset chunk offset
- yaffs_Tnode *tn;
- int theChunk = -1;
- yaffs_Tags localTags;
- int i;
- int found = 0;
- yaffs_Device *dev = in->myDev;
-
- if(!tags)
- {
- // Passed a NULL, so use our own tags space
- tags = &localTags;
- }
-
- tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
-
- if(tn)
- {
-
- theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
-
- // Now we need to do the shifting etc and search for it
- for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
- {
- yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags);
- if(tags->chunkId == chunkInInode &&
- tags->objectId == in->objectId)
- {
- // found it;
- found = 1;
- }
- else
- {
- theChunk++;
- }
- }
-
- // Delete the entry in the filestructure
- if(found)
- {
- tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0;
- }
- }
- else
- {
- T(("No level 0 found for %d\n", chunkInInode));
- }
-
- if(!found)
- {
- T(("Could not find %d to delete\n",chunkInInode));
- }
- return found ? theChunk : -1;
-}
-
-
-#if YAFFS_PARANOID
-
-static int yaffs_CheckFileSanity(yaffs_Object *in)
-{
- int chunk;
- int nChunks;
- int fSize;
- int failed = 0;
- int objId;
- yaffs_Tnode *tn;
- yaffs_Tags localTags;
- yaffs_Tags *tags = &localTags;
- int theChunk;
-
-
- if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
- {
- T(("Object not a file\n"));
- return YAFFS_FAIL;
- }
-
- objId = in->objectId;
- fSize = in->variant.fileVariant.fileSize;
- nChunks = (fSize + YAFFS_BYTES_PER_CHUNK -1)/YAFFS_BYTES_PER_CHUNK;
-
- for(chunk = 1; chunk <= nChunks; chunk++)
- {
- tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk);
-
- if(tn)
- {
-
- theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits;
-
-
- yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags);
- if(tags->chunkId == chunk &&
- tags->objectId == in->objectId)
- {
- // found it;
-
- }
- else
- {
- //T(("File problem file [%d,%d] NAND %d tags[%d,%d]\n",
- // objId,chunk,theChunk,tags->chunkId,tags->objectId);
-
- failed = 1;
-
- }
-
- }
- else
- {
- T(("No level 0 found for %d\n", chunk));
- }
- }
-
- return failed ? YAFFS_FAIL : YAFFS_OK;
-}
-
-#endif
-
-static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan)
-{
- yaffs_Tnode *tn;
- yaffs_Device *dev = in->myDev;
- int existingChunk;
- yaffs_Tags existingTags;
- yaffs_Tags newTags;
- unsigned existingSerial, newSerial;
-
-
- tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
-
- if(inScan)
- {
- // If we're scanning then we need to test for duplicates
- // NB This does not need to be efficient since it should only ever
- // happen when the power fails during a write, then only one
- // chunk should ever be affected.
-
- existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK];
-
- if(existingChunk != 0)
- {
- // We have a duplicate now we need to decide which one to use
- // To do this we get both sets of tags and compare serial numbers.
- yaffs_ReadChunkTagsFromNAND(dev,chunkInInode, &newTags);
- yaffs_ReadChunkTagsFromNAND(dev,existingChunk, &existingTags);
- newSerial = newTags.serialNumber;
- existingSerial = existingTags.serialNumber;
- if(((existingSerial+1) & 3) == newSerial)
- {
- // Use new
- // Delete the old one and drop through to update the tnode
- yaffs_DeleteChunk(dev,existingChunk);
- }
- else
- {
- // Use existing.
- // Delete the new one and return early so that the tnode isn't changed
- yaffs_DeleteChunk(dev,chunkInInode);
- return YAFFS_OK;
- }
- }
- }
-
- tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = chunkInNAND;
-
- return YAFFS_OK;
-}
-
-
-
-int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)
-{
- int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL);
-
- if(chunkInNAND >= 0)
- {
- return yaffs_ReadChunkFromNAND(in->myDev,chunkInNAND,buffer,NULL);
- }
- else
- {
- return 0;
- }
-
-}
-
-
-static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId)
-{
- int block = chunkId / YAFFS_CHUNKS_PER_BLOCK;
- int page = chunkId % YAFFS_CHUNKS_PER_BLOCK;
- yaffs_Spare spare;
-
- yaffs_SpareInitialise(&spare);
-
- spare.pageStatus = 0; // To mark it as deleted.
-
-
- yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);
-
-
- // Pull out of the management area.
- // If the whole block became dirty, this will kick off an erasure.
- if( dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
- dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL)
- {
- dev->nFreeChunks++;
-
- dev->blockInfo[block].pageBits &= ~(1 << page);
- dev->blockInfo[block].pagesInUse--;
-
- if( dev->blockInfo[block].pagesInUse == 0 &&
- dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL)
- {
- yaffs_BlockBecameDirty(dev,block);
- }
-
- }
- else
- {
- T(("Bad news deleting chunk %d\n",chunkId));
- }
-
-}
-
-
-
-
-int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve)
-{
- // Find old chunk Need to do this to get serial number
- // Write new one and patch into tree.
- // Invalidate old tags.
-
- int prevChunkId;
- yaffs_Tags prevTags;
-
- int newChunkId;
- yaffs_Tags newTags;
-
- yaffs_Device *dev = in->myDev;
-
- yaffs_CheckGarbageCollection(dev);
-
- // Get the previous chunk at this location in the file if it exists
- prevChunkId = yaffs_FindChunkInFile(in,chunkInInode,&prevTags);
-
- // Set up new tags
- newTags.chunkId = chunkInInode;
- newTags.objectId = in->objectId;
- newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;
- newTags.byteCount = nBytes;
- newTags.unusedStuff = 0xFFFFFFFF;
-
- yaffs_CalcTagsECC(&newTags);
-
-
- #if 0
- // Create new chunk in NAND
- newChunkId = yaffs_AllocatePage(dev,useReserve);
-
-
- if(newChunkId >= 0)
- {
-
-
- yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,buffer,&newTags);
-
- yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId);
-
-
- if(prevChunkId >= 0)
- {
- yaffs_DeleteChunk(dev,prevChunkId);
-
- }
-
- yaffs_CheckFileSanity(in);
-
- return newChunkId;
- }
-
-
- return -1;
-#else
-
- newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);
- if(newChunkId >= 0)
- {
- yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);
-
-
- if(prevChunkId >= 0)
- {
- yaffs_DeleteChunk(dev,prevChunkId);
-
- }
-
- yaffs_CheckFileSanity(in);
- }
- return newChunkId;
-
-#endif
-
-
-
-}
-
-
-// UpdateObjectHeader updates the header on NAND for an object.
-// If name is not NULL, then that new name is used.
-//
-int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name)
-{
-
- yaffs_Device *dev = in->myDev;
-
- int prevChunkId;
-
- int newChunkId;
- yaffs_Tags newTags;
- __u8 bufferNew[YAFFS_BYTES_PER_CHUNK];
- __u8 bufferOld[YAFFS_BYTES_PER_CHUNK];
-
- yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew;
- yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;
-
- if(!in->fake)
- {
-
- yaffs_CheckGarbageCollection(dev);
-
- memset(bufferNew,0xFF,YAFFS_BYTES_PER_CHUNK);
-
- prevChunkId = in->chunkId;
-
- if(prevChunkId >= 0)
- {
- yaffs_ReadChunkFromNAND(dev,prevChunkId,bufferOld,NULL);
- }
-
- // Header data
- oh->type = in->variantType;
-
- oh->st_mode = in->st_mode;
- oh->st_uid = in->st_uid;
- oh->st_gid = in->st_gid;
- oh->st_atime = in->st_atime;
- oh->st_mtime = in->st_mtime;
- oh->st_ctime = in->st_ctime;
-
- oh->parentObjectId = in->parent->objectId;
- oh->sum = in->sum;
- if(name && *name)
- {
- memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);
- strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);
- }
- else
- {
- memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1);
- }
-
- switch(in->variantType)
- {
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- // Should not happen
- break;
- case YAFFS_OBJECT_TYPE_FILE:
- oh->fileSize = in->variant.fileVariant.fileSize;
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId;
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- // Do nothing
- break;
- case YAFFS_OBJECT_TYPE_SYMLINK:
- strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH);
- oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
- break;
- }
-
- // Tags
- in->serial++;
- newTags.chunkId = 0;
- newTags.objectId = in->objectId;
- newTags.serialNumber = in->serial;
- newTags.byteCount = 0xFFFFFFFF;
- newTags.unusedStuff = 0xFFFFFFFF;
-
- yaffs_CalcTagsECC(&newTags);
-
-
-
-#if 0
- // Create new chunk in NAND
- newChunkId = yaffs_AllocatePage(dev,1);
-
- if(newChunkId >= 0)
- {
-
- yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,bufferNew,&newTags);
-
- in->chunkId = newChunkId;
-
- if(prevChunkId >= 0)
- {
- yaffs_DeleteChunk(dev,prevChunkId);
- }
-
- in->dirty = 0;
- return newChunkId;
- }
-
- return -1;
-#else
- // Create new chunk in NAND
- newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1);
-
- if(newChunkId >= 0)
- {
-
- in->chunkId = newChunkId;
-
- if(prevChunkId >= 0)
- {
- yaffs_DeleteChunk(dev,prevChunkId);
- }
-
- in->dirty = 0;
- }
-
- return newChunkId;
-
-#endif
- }
- return 0;
-}
-
-
-
-///////////////////////// File read/write ///////////////////////////////
-// Read and write have very similar structures.
-// In general the read/write has three parts to it
-// * An incomplete chunk to start with (if the read/write is not chunk-aligned)
-// * Some complete chunks
-// * An incomplete chunk to end off with
-//
-// Curve-balls: the first chunk might also be the last chunk.
-
-int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes)
-{
-
-// yaffs_Device *dev = in->myDev;
-
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
-
- int chunk;
- int start;
- int nToCopy;
- int n = nBytes;
- int nDone = 0;
-
- while(n > 0)
- {
- chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; // The first chunk is 1
- start = offset % YAFFS_BYTES_PER_CHUNK;
-
- // OK now check for the curveball where the start and end are in
- // the same chunk.
- if( (start + n) < YAFFS_BYTES_PER_CHUNK)
- {
- nToCopy = n;
- }
- else
- {
- nToCopy = YAFFS_BYTES_PER_CHUNK - start;
- }
-
- if(nToCopy != YAFFS_BYTES_PER_CHUNK)
- {
- // An incomplete start or end chunk (or maybe both start and end chunk)
- // Read into the local buffer then copy...
-
- yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
- memcpy(buffer,&localBuffer[start],nToCopy);
- }
- else
- {
- // A full chunk. Read directly into the supplied buffer.
- yaffs_ReadChunkDataFromObject(in,chunk,buffer);
- }
-
- n -= nToCopy;
- offset += nToCopy;
- buffer += nToCopy;
- nDone += nToCopy;
-
- }
-
- return nDone;
-}
-
-
-int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes)
-{
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
-
- int chunk;
- int start;
- int nToCopy;
- int n = nBytes;
- int nDone = 0;
- int nToWriteBack;
- int endOfWrite = offset+nBytes;
- int chunkWritten = 0;
-
- while(n > 0 && chunkWritten >= 0)
- {
- chunk = offset / YAFFS_BYTES_PER_CHUNK + 1;
- start = offset % YAFFS_BYTES_PER_CHUNK;
-
-
- // OK now check for the curveball where the start and end are in
- // the same chunk.
- if( (start + n) < YAFFS_BYTES_PER_CHUNK)
- {
- nToCopy = n;
- nToWriteBack = (start + n);
- }
- else
- {
- nToCopy = YAFFS_BYTES_PER_CHUNK - start;
- nToWriteBack = YAFFS_BYTES_PER_CHUNK;
- }
-
- if(nToCopy != YAFFS_BYTES_PER_CHUNK)
- {
- // An incomplete start or end chunk (or maybe both start and end chunk)
- // Read into the local buffer then copy, then copy over and write back.
-
- yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
-
- memcpy(&localBuffer[start],buffer,nToCopy);
-
- chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0);
-
- T(("Write with readback to chunk %d %d\n",chunk,chunkWritten));
-
- }
- else
- {
- // A full chunk. Write directly from the supplied buffer.
- chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0);
- T(("Write to chunk %d %d\n",chunk,chunkWritten));
- }
-
- if(chunkWritten >= 0)
- {
- n -= nToCopy;
- offset += nToCopy;
- buffer += nToCopy;
- nDone += nToCopy;
- }
-
- }
-
- // Update file object
-
- if(endOfWrite > in->variant.fileVariant.fileSize)
- {
- in->variant.fileVariant.fileSize = endOfWrite;
- }
-
- in->dirty = 1;
- in->st_mtime = CURRENT_TIME;
-
- return nDone;
-}
-
-
-int yaffs_ResizeFile(yaffs_Object *in, int newSize)
-{
- int i;
- int chunkId;
- int oldFileSize = in->variant.fileVariant.fileSize;
- int sizeOfLastChunk = newSize % YAFFS_BYTES_PER_CHUNK;
-
- yaffs_Device *dev = in->myDev;
-
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
-
- if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
- {
- return yaffs_GetFileSize(in);
- }
-
- if(newSize < oldFileSize)
- {
-
- int lastDel = 1 + oldFileSize/YAFFS_BYTES_PER_CHUNK;
-
- int startDel = 1 + (newSize + YAFFS_BYTES_PER_CHUNK - 1)/
- YAFFS_BYTES_PER_CHUNK;
-
- for(i = startDel; i <= lastDel; i++)
- {
- // NB this could be optimised somewhat,
- // eg. could retrieve the tags and write them without
- // using yaffs_DeleteChunk
- chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);
- if(chunkId < 0 || chunkId >= (dev->endBlock * 32))
- {
- T(("Found daft chunkId %d for %d\n",chunkId,i));
- }
- else
- {
- yaffs_DeleteChunk(dev,chunkId);
- }
- }
-
-
- if(sizeOfLastChunk != 0)
- {
- int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK;
-
- // Got to read and rewrite the last chunk with its new size.
- yaffs_ReadChunkDataFromObject(in,lastChunk,localBuffer);
-
- yaffs_WriteChunkDataToObject(in,lastChunk,localBuffer,sizeOfLastChunk,1);
-
- }
-
- in->variant.fileVariant.fileSize = newSize;
-
- yaffs_PruneFileStructure(dev,&in->variant.fileVariant);
-
- return newSize;
-
- }
- else
- {
- return oldFileSize;
- }
-}
-
-
-loff_t yaffs_GetFileSize(yaffs_Object *obj)
-{
- obj = yaffs_GetEquivalentObject(obj);
-
- switch(obj->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- return obj->variant.fileVariant.fileSize;
- case YAFFS_OBJECT_TYPE_SYMLINK:
- return strlen(obj->variant.symLinkVariant.alias);
- default:
- return 0;
- }
-}
-
-
-
-// yaffs_FlushFile() updates the file's
-// objectId in NAND
-
-int yaffs_FlushFile(yaffs_Object *in)
-{
- int retVal;
- if(in->dirty)
- {
- T(("flushing object header\n"));
- retVal = yaffs_UpdateObjectHeader(in,NULL);
- }
- else
- {
- retVal = YAFFS_OK;
- }
-
- return retVal;
-
-}
-
-
-static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)
-{
- yaffs_RemoveObjectFromDirectory(in);
- yaffs_DeleteChunk(in->myDev,in->chunkId);
- yaffs_FreeObject(in);
- return YAFFS_OK;
-
-}
-
-// yaffs_DeleteFile deletes the whole file data
-// and the inode associated with the file.
-// It does not delete the links associated with the file.
-static int yaffs_DeleteFile(yaffs_Object *in)
-{
- // Delete the file data & tnodes
- yaffs_ResizeFile(in,0);
- yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
-
- return yaffs_DoGenericObjectDeletion(in);
-}
-
-static int yaffs_DeleteDirectory(yaffs_Object *in)
-{
- //First check that the directory is empty.
- if(list_empty(&in->variant.directoryVariant.children))
- {
- return yaffs_DoGenericObjectDeletion(in);
- }
-
- return YAFFS_FAIL;
-
-}
-
-static int yaffs_DeleteSymLink(yaffs_Object *in)
-{
- YFREE(in->variant.symLinkVariant.alias);
-
- return yaffs_DoGenericObjectDeletion(in);
-}
-
-static int yaffs_DeleteHardLink(yaffs_Object *in)
-{
- // remove this hardlink from the list assocaited with the equivalent
- // object
- list_del(&in->hardLinks);
- return yaffs_DoGenericObjectDeletion(in);
-}
-
-
-static int yaffs_UnlinkWorker(yaffs_Object *obj)
-{
-
-
- if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
- {
- return yaffs_DeleteHardLink(obj);
- }
- else if(!list_empty(&obj->hardLinks))
- {
-#if 0
- // Curve ball: We're unlinking an object that has a hardlink.
- // Therefore we can't really delete the object.
- // Instead, we do the following:
- // - Select a hardlink.
- // - Re-type a hardlink as the equivalent object and populate the fields, including the
- // objectId. Updating the object id is important so that all the hardlinks do not need
- // to be rewritten.
- // - Update the equivalet object pointers.
- // - Delete all object.
-
- yaffs_Object *hl;
- struct list_head *i;
-
-
- yaffs_RemoveObjectFromDirectory(obj);
-
-
-
- hl = list_entry(obj->hardLinks.next, yaffs_Object,hardLinks);
-
- hl->dirty = 1;
- hl->st_mode = obj->st_mode;
- hl->st_uid = obj->st_uid;
- hl->st_gid = obj->st_gid;
- hl->st_atime = obj->st_atime;
- hl->st_mtime = obj->st_mtime;
- hl->st_ctime = obj->st_ctime;
-
- hl->variantType = obj->variantType;
-
- switch(hl->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- case YAFFS_OBJECT_TYPE_SYMLINK:
- // These types are OK to just copy across.
- hl->variant = obj->variant;
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- // Fix the list up
- list_add(&hl->variant.directoryVariant.children,
- &obj->variant.directoryVariant.children);
- list_del(&obj->variant.directoryVariant.children);
-
- // Now change all the directory children to point to the new parent.
- list_for_each(i,&hl->variant.directoryVariant.children)
- {
- list_entry(i,yaffs_Object,siblings)->parent = hl;
- }
- break;
-
- case YAFFS_OBJECT_TYPE_HARDLINK:
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- // Should not be either of these types.
- }
-
- // Now fix up the hardlink chain
- list_del(&obj->hardLinks);
-
- list_for_each(i,&hl->hardLinks)
- {
- list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObject = hl;
- list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObjectId = hl->objectId;
- }
-
- // Now fix up the hash links.
- yaffs_UnhashObject(hl);
- hl->objectId = obj->objectId;
- yaffs_HashObject(hl);
-
- // Update the hardlink which has become an object
- yaffs_UpdateObjectHeader(hl,NULL);
-
- // Finally throw away the deleted object
- yaffs_DeleteChunk(obj->myDev,obj->chunkId);
- yaffs_FreeObject(obj);
-
- return YAFFS_OK;
-#else
- // Curve ball: We're unlinking an object that has a hardlink.
- //
- // This problem arises because we are not strictly following
- // The Linux link/inode model.
- //
- // We can't really delete the object.
- // Instead, we do the following:
- // - Select a hardlink.
- // - Unhook it from the hard links
- // - Unhook it from its parent directory (so that the rename can work)
- // - Rename the object to the hardlink's name.
- // - Delete the hardlink
-
-
- yaffs_Object *hl;
- int retVal;
- char name[YAFFS_MAX_NAME_LENGTH+1];
-
- hl = list_entry(obj->hardLinks.next,yaffs_Object,hardLinks);
-
- list_del_init(&hl->hardLinks);
- list_del_init(&hl->siblings);
-
- yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);
-
- retVal = yaffs_ChangeObjectName(obj, hl->parent, name);
-
- if(retVal == YAFFS_OK)
- {
- retVal = yaffs_DoGenericObjectDeletion(hl);
- }
- return retVal;
-
-#endif
-
-
- }
- else
- {
- switch(obj->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- return yaffs_DeleteFile(obj);
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- return yaffs_DeleteDirectory(obj);
- break;
- case YAFFS_OBJECT_TYPE_SYMLINK:
- return yaffs_DeleteSymLink(obj);
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- default:
- return YAFFS_FAIL;
- }
- }
-}
-
-int yaffs_Unlink(yaffs_Object *dir, const char *name)
-{
- yaffs_Object *obj;
-
- obj = yaffs_FindObjectByName(dir,name);
-
- if(obj && obj->unlinkAllowed)
- {
- return yaffs_UnlinkWorker(obj);
- }
-
- return YAFFS_FAIL;
-
-}
-
-//////////////// Initialisation Scanning /////////////////
-
-
-static int yaffs_Scan(yaffs_Device *dev)
-{
- yaffs_Spare spare;
- yaffs_Tags tags;
- int blk;
- int chunk;
- int c;
- int deleted;
- yaffs_BlockState state;
- yaffs_Object *hardList = NULL;
- yaffs_Object *hl;
-
-// int inuse;
-// __u32 pageBits;
-
- yaffs_ObjectHeader *oh;
- yaffs_Object *in;
- yaffs_Object *parent;
-
- __u8 chunkData[YAFFS_BYTES_PER_CHUNK];
-
-
- // Scan all the blocks...
-
- for(blk = dev->startBlock; blk <= dev->endBlock; blk++)
- {
- deleted = 0;
- dev->blockInfo[blk].pageBits = 0;
- dev->blockInfo[blk].pagesInUse = 0;
- state = YAFFS_BLOCK_STATE_SCANNING;
-
- // Read each chunk in the block.
-
- for(c = 0; c < YAFFS_CHUNKS_PER_BLOCK &&
- state == YAFFS_BLOCK_STATE_SCANNING; c++)
- {
- // Read the spare area and decide what to do
- chunk = blk * YAFFS_CHUNKS_PER_BLOCK + c;
-
- yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare);
-
-
- // Is this a valid block?
- if(yaffs_countBits[spare.blockStatus] >= 7)
- {
- // This block looks ok, now what's in this chunk?
- yaffs_GetTagsFromSpare(&spare,&tags);
-
- if(yaffs_countBits[spare.pageStatus] < 6)
- {
- // A deleted chunk
- deleted++;
- dev->nFreeChunks ++;
- T((" %d %d deleted\n",blk,c));
- }
- else if(tags.objectId == YAFFS_UNUSED_OBJECT_ID)
- {
- // An unassigned chunk in the block
- // This means that either the block is empty or
- // this is the one being allocated from
-
- if(c == 0)
- {
- // the block is unused
- state = YAFFS_BLOCK_STATE_EMPTY;
- dev->nErasedBlocks++;
- }
- else
- {
- // this is the block being allocated from
- T((" allocating %d %d\n",blk,c));
- state = YAFFS_BLOCK_STATE_ALLOCATING;
- dev->allocationBlock = blk;
- dev->allocationPage = c;
- }
-
- dev->nFreeChunks += (YAFFS_CHUNKS_PER_BLOCK - c);
- }
- else if(tags.chunkId > 0)
- {
- // A data chunk.
- dev->blockInfo[blk].pageBits |= (1 << c);
- dev->blockInfo[blk].pagesInUse++;
-
- in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
- // PutChunkIntoFIle checks for a clash (two data chunks with
- // the same chunkId).
- yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);
- T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));
- }
- else
- {
- // chunkId == 0, so it is an ObjectHeader.
- // Thus, we read in the object header and make the object
- dev->blockInfo[blk].pageBits |= (1 << c);
- dev->blockInfo[blk].pagesInUse++;
-
- yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL);
-
- oh = (yaffs_ObjectHeader *)chunkData;
-
- in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
-
- if(in->valid)
- {
- // We have already filled this one. We have a duplicate and need to resolve it.
-
- unsigned existingSerial = in->serial;
- unsigned newSerial = tags.serialNumber;
-
- if(((existingSerial+1) & 3) == newSerial)
- {
- // Use new one - destroy the exisiting one
- yaffs_DeleteChunk(dev,in->chunkId);
- in->valid = 0;
- }
- else
- {
- // Use existing - destroy this one.
- yaffs_DeleteChunk(dev,chunk);
- }
- }
-
- if(!in->valid)
- {
- // we need to load this info
-
- in->valid = 1;
- in->variantType = oh->type;
-
- in->st_mode = oh->st_mode;
- in->st_uid = oh->st_uid;
- in->st_gid = oh->st_gid;
- in->st_atime = oh->st_atime;
- in->st_mtime = oh->st_mtime;
- in->st_ctime = oh->st_ctime;
- in->chunkId = chunk;
-
- in->sum = oh->sum;
- in->dirty = 0;
-
- // directory stuff...
- // hook up to parent
-
- parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY);
- if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN)
- {
- // Set up as a directory
- parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY;
- INIT_LIST_HEAD(&parent->variant.directoryVariant.children);
- }
- else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
- {
- // Hoosterman, another problem....
- // We're trying to use a non-directory as a directory
- // Todo ... handle
- }
-
- yaffs_AddObjectToDirectory(parent,in);
-
- // Note re hardlinks.
- // Since we might scan a hardlink before its equivalent object is scanned
- // we put them all in a list.
- // After scanning is complete, we should have all the objects, so we run through this
- // list and fix up all the chains.
-
- switch(in->variantType)
- {
- case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem
- break;
- case YAFFS_OBJECT_TYPE_FILE:
- in->variant.fileVariant.fileSize = oh->fileSize;
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
- (yaffs_Object *)(in->hardLinks.next) = hardList;
- hardList = in;
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY: // Do nothing
- break;
- case YAFFS_OBJECT_TYPE_SYMLINK: // Do nothing
- in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);
- break;
- }
- T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));
- }
- }
- }
- else
- {
- // it's a bad block
- state = YAFFS_BLOCK_STATE_DEAD;
- }
- }
-
- if(state == YAFFS_BLOCK_STATE_SCANNING)
- {
- // If we got this far while scanning, then the block is fully allocated.
- state = YAFFS_BLOCK_STATE_FULL;
- }
-
- dev->blockInfo[blk].blockState = state;
-
- // Now let's see if it was dirty
- if( dev->blockInfo[blk].pagesInUse == 0 &&
- dev->blockInfo[blk].blockState == YAFFS_BLOCK_STATE_FULL)
- {
- yaffs_BlockBecameDirty(dev,blk);
- }
-
- }
-
- // Fix up the hard link chains.
- // We should now have scanned all the objects, now it's time to add these
- // hardlinks.
- while(hardList)
- {
- hl = hardList;
- hardList = (yaffs_Object *)(hardList->hardLinks.next);
-
- in = yaffs_FindObjectByNumber(dev,hl->variant.hardLinkVariant.equivalentObjectId);
-
- if(in)
- {
- // Add the hardlink pointers
- hl->variant.hardLinkVariant.equivalentObject=in;
- list_add(&hl->hardLinks,&in->hardLinks);
- }
- else
- {
- //Todo Need to report/handle this better.
- // Got a problem... hardlink to a non-existant object
- hl->variant.hardLinkVariant.equivalentObject=NULL;
- INIT_LIST_HEAD(&hl->hardLinks);
-
- }
-
- }
-
-
-
- return YAFFS_OK;
-}
-
-
-////////////////////////// Directory Functions /////////////////////////
-
-
-static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj)
-{
-
- if(obj->siblings.prev == NULL)
- {
- // Not initialised
- INIT_LIST_HEAD(&obj->siblings);
-
- }
- else if(!list_empty(&obj->siblings))
- {
- // If it is holed up somewhere else, un hook it
- list_del_init(&obj->siblings);
- }
- // Now add it
- list_add(&obj->siblings,&directory->variant.directoryVariant.children);
- obj->parent = directory;
-}
-
-static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)
-{
- list_del_init(&obj->siblings);
- obj->parent = NULL;
-}
-
-yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const char *name)
-{
- int sum;
-
- struct list_head *i;
- __u8 buffer[YAFFS_BYTES_PER_CHUNK];
- yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
-
- yaffs_Object *l;
-
- sum = yaffs_CalcNameSum(name);
-
- list_for_each(i,&directory->variant.directoryVariant.children)
- {
- l = list_entry(i, yaffs_Object,siblings);
-
- // Special case for lost-n-found
- if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)
- {
- if(strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)
- {
- return l;
- }
- }
- else if(l->sum == sum)
- {
- // Do a real check
- yaffs_ReadChunkFromNAND(l->myDev,l->chunkId,buffer,NULL);
- if(strcmp(name,oh->name) == 0)
- {
- return l;
- }
-
-
- }
- }
-
- return NULL;
-}
-
-
-int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *))
-{
- struct list_head *i;
- yaffs_Object *l;
-
-
- list_for_each(i,&theDir->variant.directoryVariant.children)
- {
- l = list_entry(i, yaffs_Object,siblings);
- if(!fn(l))
- {
- return YAFFS_FAIL;
- }
- }
-
- return YAFFS_OK;
-
-}
-
-
-// GetEquivalentObject dereferences any hard links to get to the
-// actual object.
-
-yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj)
-{
- if(obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
- {
- // We want the object id of the equivalent object, not this one
- obj = obj->variant.hardLinkVariant.equivalentObject;
- }
- return obj;
-
-}
-
-int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize)
-{
- memset(name,0,buffSize);
-
- if(obj->objectId == YAFFS_OBJECTID_LOSTNFOUND)
- {
- strncpy(name,YAFFS_LOSTNFOUND_NAME,buffSize - 1);
- }
- else
- {
- __u8 buffer[YAFFS_BYTES_PER_CHUNK];
- yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
-
- memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
-
- if(obj->chunkId >= 0)
- {
- yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);
- }
- strncpy(name,oh->name,buffSize - 1);
- }
-
- return strlen(name);
-}
-
-int yaffs_GetObjectFileLength(yaffs_Object *obj)
-{
-
- // Dereference any hard linking
- obj = yaffs_GetEquivalentObject(obj);
-
- if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)
- {
- return obj->variant.fileVariant.fileSize;
- }
- if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
- {
- return strlen(obj->variant.symLinkVariant.alias);
- }
- else
- {
- // Only a directory should drop through to here
- return YAFFS_BYTES_PER_CHUNK;
- }
-}
-
-int yaffs_GetObjectLinkCount(yaffs_Object *obj)
-{
- int count = 1; // the object itself
- struct list_head *i;
-
- list_for_each(i,&obj->hardLinks)
- {
- count++;
- }
- return count;
-
-}
-
-
-int yaffs_GetObjectInode(yaffs_Object *obj)
-{
- obj = yaffs_GetEquivalentObject(obj);
-
- return obj->objectId;
-}
-
-unsigned yaffs_GetObjectType(yaffs_Object *obj)
-{
- obj = yaffs_GetEquivalentObject(obj);
-
- switch(obj->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE: return DT_REG; break;
- case YAFFS_OBJECT_TYPE_DIRECTORY: return DT_DIR; break;
- case YAFFS_OBJECT_TYPE_SYMLINK: return DT_LNK; break;
- case YAFFS_OBJECT_TYPE_HARDLINK: return DT_REG; break;
- default: return DT_REG; break;
- }
-}
-
-char *yaffs_GetSymlinkAlias(yaffs_Object *obj)
-{
- obj = yaffs_GetEquivalentObject(obj);
- if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
- {
- return yaffs_CloneString(obj->variant.symLinkVariant.alias);
- }
- else
- {
- return yaffs_CloneString("");
- }
-}
-
-
-int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)
-{
- unsigned int valid = attr->ia_valid;
-
- if(valid & ATTR_MODE) obj->st_mode = attr->ia_mode;
- if(valid & ATTR_UID) obj->st_uid = attr->ia_uid;
- if(valid & ATTR_GID) obj->st_gid = attr->ia_gid;
-
- if(valid & ATTR_ATIME) obj->st_atime = attr->ia_atime;
- if(valid & ATTR_CTIME) obj->st_ctime = attr->ia_ctime;
- if(valid & ATTR_MTIME) obj->st_mtime = attr->ia_mtime;
-
- if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);
-
- yaffs_UpdateObjectHeader(obj,NULL);
-
- return YAFFS_OK;
-
-}
-int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)
-{
- unsigned int valid = 0;
-
- attr->ia_mode = obj->st_mode; valid |= ATTR_MODE;
- attr->ia_uid = obj->st_uid; valid |= ATTR_UID;
- attr->ia_gid = obj->st_gid; valid |= ATTR_GID;
-
- attr->ia_atime = obj->st_atime; valid |= ATTR_ATIME;
- attr->ia_ctime = obj->st_ctime; valid |= ATTR_CTIME;
- attr->ia_mtime = obj->st_mtime; valid |= ATTR_MTIME;
-
- attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;
-
- attr->ia_valid = valid;
-
- return YAFFS_OK;
-
-}
-
-
-
-int yaffs_DumpObject(yaffs_Object *obj)
-{
- __u8 buffer[YAFFS_BYTES_PER_CHUNK];
- char name[256];
-// yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
-
- memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
-
- if(obj->chunkId >= 0)
- {
- yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);
- }
-
- yaffs_GetObjectName(obj,name,256);
-
- YPRINTF(("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n",
- obj->objectId,yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial,
- obj->sum, obj->chunkId, yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));
-
-#if 0
- YPRINTF(("Object %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d\n",
- obj->objectId, oh->name, obj->dirty, obj->valid, obj->serial,
- obj->sum, obj->chunkId));
- switch(obj->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- YPRINTF((" FILE length %d\n",obj->variant.fileVariant.fileSize));
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- YPRINTF((" DIRECTORY\n"));
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK: //todo
- case YAFFS_OBJECT_TYPE_SYMLINK:
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- default:
- }
-#endif
-
- return YAFFS_OK;
-}
-
-
-///////////////////////// Initialisation code ///////////////////////////
-
-
-
-int yaffs_GutsInitialise(yaffs_Device *dev)
-{
- unsigned nChunks,x;
- int bits;
-
-
- dev = dev;
-
- if(!yaffs_CheckStructures())
- {
- return YAFFS_FAIL;
- }
-
-
- // OK now calculate a few things for the device
- // Calculate chunkGroupBits.
- // If there are 64k or less chunks then this is 1
- // Else it is log2(nChunks) - 16
- //
- x = nChunks = YAFFS_CHUNKS_PER_BLOCK * dev->nBlocks;
-
- for(bits = 0, x = nChunks; (x & 1) == 0; bits++)
- {
- x >>= 1;
- }
-
- if( x != 1)
- {
- // Not a power of 2
- YPRINTF(("nBlocks should be a power of 2 but is %u\n",
- dev->nBlocks));
- return YAFFS_FAIL;
- }
-
- if(bits <= 16)
- {
- dev->chunkGroupBits = 0;
- dev->chunkGroupSize = 1;
- }
- else
- {
- dev->chunkGroupBits = bits - 16;
- dev->chunkGroupSize = nChunks/0x10000;
- }
-
- // More device initialisation
- dev->garbageCollectionRequired = 0;
- dev->currentDirtyChecker = 0;
-
- yaffs_InitialiseBlocks(dev);
-
- yaffs_InitialiseTnodes(dev);
-
- yaffs_InitialiseObjects(dev);
-
-
- // Initialise the root and lost and found directories
- dev->lostNFoundDir = dev->rootDir = NULL;
- dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR);
- dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_ROOT_MODE | S_IFDIR);
- yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir);
-
- // Now scan the flash.
- yaffs_Scan(dev);
-
-
- return YAFFS_OK;
-
-}
-
-void yaffs_Deinitialise(yaffs_Device *dev)
-{
- yaffs_DeinitialiseBlocks(dev);
- yaffs_DeinitialiseTnodes(dev);
- yaffs_DeinitialiseObjects(dev);
-
-}
-
-int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
-{
- int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS);
-
- return (nFree < 0) ? 0 : nFree;
-
-}
-
-
-/////////////////// YAFFS test code //////////////////////////////////
-
-#define yaffs_CheckStruct(structure,syze, name) \
- if(sizeof(structure) != syze) \
- { YPRINTF(("%s should be %d but is %d\n",name,syze,sizeof(structure))); \
- return YAFFS_FAIL; \
- }
-
-
-static int yaffs_CheckStructures(void)
-{
- yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags")
- yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion")
- yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare")
- yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode")
- yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader")
-
-
- return YAFFS_OK;
-}
-
-void yaffs_GutsTest(yaffs_Device *dev)
-{
-
- if(yaffs_CheckStructures() != YAFFS_OK)
- {
- YPRINTF(("One or more structures malformed-- aborting\n"));
- }
- else
- {
- YPRINTF(("Structures OK\n"));
- }
-
- yaffs_TnodeTest(dev);
- yaffs_ObjectTest(dev);
-}
-
-
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ * yaffs_guts.c The main guts of YAFFS
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+ //yaffs_guts.c
+
+#include "yportenv.h"
+
+#include "yaffsinterface.h"
+#include "yaffs_guts.h"
+
+
+
+// External functions for ECC on data
+void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
+int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+
+
+// countBits is a quick way of counting the number of bits in a byte.
+// ie. countBits[n] holds the number of 1 bits in a byte with the value n.
+
+static const char yaffs_countBits[256] =
+{
+0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
+};
+
+
+
+// Device info
+//static yaffs_Device *yaffs_device;
+//yaffs_Object *yaffs_rootDir;
+//yaffs_Object *yaffs_lostNFound;
+
+
+
+// Local prototypes
+static int yaffs_CheckObjectHashSanity(yaffs_Device *dev);
+static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr);
+static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);
+static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan);
+
+static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type);
+static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj);
+static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name);
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId);
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);
+static int yaffs_CheckStructures(void);
+
+loff_t yaffs_GetFileSize(yaffs_Object *obj);
+
+static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted);
+static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted);
+
+
+static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);
+
+#ifdef YAFFS_PARANOID
+static int yaffs_CheckFileSanity(yaffs_Object *in);
+#else
+#define yaffs_CheckFileSanity(in)
+#endif
+
+static int __inline__ yaffs_HashFunction(int n)
+{
+ return (n % YAFFS_NOBJECT_BUCKETS);
+}
+
+
+yaffs_Object *yaffs_Root(yaffs_Device *dev)
+{
+ return dev->rootDir;
+}
+
+yaffs_Object *yaffs_LostNFound(yaffs_Device *dev)
+{
+ return dev->lostNFoundDir;
+}
+
+
+static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
+{
+ dev->nPageWrites++;
+ return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);
+}
+
+int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
+{
+ int retVal;
+ __u8 calcEcc[3];
+ yaffs_Spare localSpare;
+ int eccResult1,eccResult2;
+
+ dev->nPageReads++;
+
+ if(!spare && data)
+ {
+ // If we don't have a real spare, then we use a local one.
+ // Need this for the calculation of the ecc
+ spare = &localSpare;
+ }
+
+ retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
+ if(data)
+ {
+ // Do ECC correction
+ //Todo handle any errors
+ nand_calculate_ecc(data,calcEcc);
+ eccResult1 = nand_correct_data (data,spare->ecc1, calcEcc);
+ nand_calculate_ecc(&data[256],calcEcc);
+ eccResult2 = nand_correct_data (&data[256],spare->ecc2, calcEcc);
+
+ if(eccResult1>0)
+ {
+ T((TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
+ }
+ else if(eccResult1<0)
+ {
+ T((TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
+ }
+
+ if(eccResult2>0)
+ {
+ T((TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
+ }
+ else if(eccResult2<0)
+ {
+ T((TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
+ }
+ }
+ return retVal;
+}
+
+#ifdef YAFFS_PARANOID
+
+static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)
+{
+ static int init = 0;
+ static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];
+ static __u8 data[YAFFS_BYTES_PER_CHUNK];
+ static __u8 spare[16];
+
+ int retVal;
+
+ retVal = YAFFS_OK;
+
+ dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare);
+
+
+
+ if(!init)
+ {
+ memset(cmpbuf,0xff,YAFFS_BYTES_PER_CHUNK);
+ init = 1;
+ }
+
+ if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) retVal = YAFFS_FAIL;
+ if(memcmp(cmpbuf,spare,16)) retVal = YAFFS_FAIL;
+
+ return retVal;
+
+}
+
+#endif
+
+
+int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
+{
+ dev->nBlockErasures++;
+ return dev->eraseBlockInNAND(dev,blockInNAND);
+}
+
+int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
+{
+ return dev->initialiseNAND(dev);
+}
+
+static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_Spare *spare,int useReserve)
+{
+ int chunk;
+
+ int writeOk = 0;
+ int attempts = 0;
+
+ unsigned char rbData[YAFFS_BYTES_PER_CHUNK];
+ yaffs_Spare rbSpare;
+
+ do{
+ chunk = yaffs_AllocateChunk(dev,useReserve);
+
+ if(chunk >= 0)
+ {
+ writeOk = yaffs_WriteChunkToNAND(dev,chunk,data,spare);
+ attempts++;
+ if(writeOk)
+ {
+ // Readback & verify
+ // If verify fails, then delete this chunk and try again
+ // To verify we compare everything except the block and
+ // page status bytes.
+ yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare);
+
+ if(memcmp(data,rbData,YAFFS_BYTES_PER_CHUNK) != 0 ||
+ spare->tagByte0 != rbSpare.tagByte0 ||
+ spare->tagByte1 != rbSpare.tagByte1 ||
+ spare->tagByte2 != rbSpare.tagByte2 ||
+ spare->tagByte3 != rbSpare.tagByte3 ||
+ spare->tagByte4 != rbSpare.tagByte4 ||
+ spare->tagByte5 != rbSpare.tagByte5 ||
+ spare->tagByte6 != rbSpare.tagByte6 ||
+ spare->tagByte7 != rbSpare.tagByte7 ||
+ spare->ecc1[0] != rbSpare.ecc1[0] ||
+ spare->ecc1[1] != rbSpare.ecc1[1] ||
+ spare->ecc1[2] != rbSpare.ecc1[2] ||
+ spare->ecc2[0] != rbSpare.ecc2[0] ||
+ spare->ecc2[1] != rbSpare.ecc2[1] ||
+ spare->ecc2[2] != rbSpare.ecc2[2] )
+ {
+ // Didn't verify
+ yaffs_DeleteChunk(dev,chunk);
+ T((TSTR("**>> yaffs write failed on chunk %d" TENDSTR), chunk));
+
+ writeOk = 0;
+ }
+
+ }
+ }
+ } while(chunk >= 0 && ! writeOk);
+
+ if(attempts > 1)
+ {
+ T((TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));
+ dev->nRetriedWrites+= (attempts - 1);
+ }
+
+ return chunk;
+}
+
+
+
+
+///////////////////////// Object management //////////////////
+// List of spare objects
+// The list is hooked together using the first pointer
+// in the object
+
+// static yaffs_Object *yaffs_freeObjects = NULL;
+
+// static int yaffs_nFreeObjects;
+
+// static yaffs_ObjectList *yaffs_allocatedObjectList = NULL;
+
+// static yaffs_ObjectBucket yaffs_objectBucket[YAFFS_NOBJECT_BUCKETS];
+
+
+static __u16 yaffs_CalcNameSum(const char *name)
+{
+ __u16 sum = 0;
+ __u16 i = 1;
+
+ __u8 *bname = (__u8 *)name;
+
+ while (*bname)
+ {
+ sum += (*bname) * i;
+ i++;
+ bname++;
+ }
+ return sum;
+}
+
+
+void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
+{
+ nand_calculate_ecc (data , spare->ecc1);
+ nand_calculate_ecc (&data[256] , spare->ecc2);
+}
+
+void yaffs_CalcTagsECC(yaffs_Tags *tags)
+{
+ // Calculate an ecc
+
+ unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
+ unsigned i,j;
+ unsigned ecc = 0;
+ unsigned bit = 0;
+
+ tags->ecc = 0;
+
+ for(i = 0; i < 8; i++)
+ {
+ for(j = 1; j &0x7f; j<<=1)
+ {
+ bit++;
+ if(b[i] & j)
+ {
+ ecc ^= bit;
+ }
+ }
+ }
+
+ tags->ecc = ecc;
+
+
+}
+
+void yaffs_CheckECCOnTags(yaffs_Tags *tags)
+{
+ unsigned ecc = tags->ecc;
+
+ yaffs_CalcTagsECC(tags);
+
+ ecc ^= tags->ecc;
+
+ if(ecc)
+ {
+ // Needs fixing
+ unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
+
+ ecc--;
+
+ b[ecc / 8] ^= (1 << (ecc & 7));
+
+ // Now recvalc the ecc
+ yaffs_CalcTagsECC(tags);
+ }
+}
+
+
+///////////////////////// TNODES ///////////////////////
+
+// List of spare tnodes
+// The list is hooked together using the first pointer
+// in the tnode.
+
+//static yaffs_Tnode *yaffs_freeTnodes = NULL;
+
+// static int yaffs_nFreeTnodes;
+
+//static yaffs_TnodeList *yaffs_allocatedTnodeList = NULL;
+
+
+
+// yaffs_CreateTnodes creates a bunch more tnodes and
+// adds them to the tnode free list.
+// Don't use this function directly
+
+static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes)
+{
+ int i;
+ yaffs_Tnode *newTnodes;
+ yaffs_TnodeList *tnl;
+
+ if(nTnodes < 1) return YAFFS_OK;
+
+ // make these things
+
+ newTnodes = YMALLOC(nTnodes * sizeof(yaffs_Tnode));
+
+ if (!newTnodes)
+ {
+ YALERT("Could not malloc tnodes");
+ return YAFFS_FAIL;
+ }
+
+ // Hook them into the free list
+ for(i = 0; i < nTnodes - 1; i++)
+ {
+ newTnodes[i].internal[0] = &newTnodes[i+1];
+ }
+
+ newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
+ dev->freeTnodes = newTnodes;
+ dev->nFreeTnodes+= nTnodes;
+ dev->nTnodesCreated += nTnodes;
+
+ // Now add this bunch of tnodes to a list for freeing up.
+
+ tnl = YMALLOC(sizeof(yaffs_TnodeList));
+ if(!tnl)
+ {
+ YALERT("Could not add tnodes to management list");
+ }
+ else
+ {
+ tnl->tnodes = newTnodes;
+ tnl->next = dev->allocatedTnodeList;
+ dev->allocatedTnodeList = tnl;
+ }
+
+
+ YINFO("Tnodes created");
+
+
+ return YAFFS_OK;
+}
+
+
+// GetTnode gets us a clean tnode. Tries to make allocate more if we run out
+static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
+{
+ yaffs_Tnode *tn = NULL;
+
+ // If there are none left make more
+ if(!dev->freeTnodes)
+ {
+ yaffs_CreateTnodes(dev,YAFFS_ALLOCATION_NTNODES);
+ }
+
+ if(dev->freeTnodes)
+ {
+ tn = dev->freeTnodes;
+ dev->freeTnodes = dev->freeTnodes->internal[0];
+ dev->nFreeTnodes--;
+ // zero out
+ memset(tn,0,sizeof(yaffs_Tnode));
+ }
+
+
+ return tn;
+}
+
+
+// FreeTnode frees up a tnode and puts it back on the free list
+static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn)
+{
+ tn->internal[0] = dev->freeTnodes;
+ dev->freeTnodes = tn;
+ dev->nFreeTnodes++;
+}
+
+
+static void yaffs_DeinitialiseTnodes(yaffs_Device*dev)
+{
+ // Free the list of allocated tnodes
+
+ while(dev->allocatedTnodeList)
+ {
+ YFREE(dev->allocatedTnodeList->tnodes);
+ dev->allocatedTnodeList = dev->allocatedTnodeList->next;
+ }
+
+ dev->freeTnodes = NULL;
+ dev->nFreeTnodes = 0;
+}
+
+static void yaffs_InitialiseTnodes(yaffs_Device*dev)
+{
+ dev->allocatedTnodeList = NULL;
+ dev->freeTnodes = NULL;
+ dev->nFreeTnodes = 0;
+ dev->nTnodesCreated = 0;
+
+}
+
+void yaffs_TnodeTest(yaffs_Device *dev)
+{
+
+ int i;
+ int j;
+ yaffs_Tnode *tn[1000];
+
+ YINFO("Testing TNodes");
+
+ for(j = 0; j < 50; j++)
+ {
+ for(i = 0; i < 1000; i++)
+ {
+ tn[i] = yaffs_GetTnode(dev);
+ if(!tn[i])
+ {
+ YALERT("Getting tnode failed");
+ }
+ }
+ for(i = 0; i < 1000; i+=3)
+ {
+ yaffs_FreeTnode(dev,tn[i]);
+ tn[i] = NULL;
+ }
+
+ }
+}
+
+////////////////// END OF TNODE MANIPULATION ///////////////////////////
+
+/////////////// Functions to manipulate the look-up tree (made up of tnodes)
+// The look up tree is represented by the top tnode and the number of topLevel
+// in the tree. 0 means only the level 0 tnode is in the tree.
+
+
+// FindLevel0Tnode finds the level 0 tnode, if one exists.
+// Used when reading.....
+static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure *fStruct, __u32 chunkId)
+{
+
+ yaffs_Tnode *tn = fStruct->top;
+ __u32 i;
+ int requiredTallness;
+ int level = fStruct->topLevel;
+
+ // Check sane level and chunk Id
+ if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
+ {
+ char str[50];
+ sprintf(str,"Bad level %d",level);
+ YALERT(str);
+ return NULL;
+ }
+
+ if(chunkId > YAFFS_MAX_CHUNK_ID)
+ {
+ char str[50];
+ sprintf(str,"Bad chunkId %d",chunkId);
+ YALERT(str);
+ return NULL;
+ }
+
+ // First check we're tall enough (ie enough topLevel)
+
+ i = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);
+ requiredTallness = 0;
+ while(i)
+ {
+ i >>= YAFFS_TNODES_INTERNAL_BITS;
+ requiredTallness++;
+ }
+
+
+ if(requiredTallness > fStruct->topLevel)
+ {
+ // Not tall enough, so we can't find it, return NULL.
+ return NULL;
+ }
+
+
+ // Traverse down to level 0
+ while (level > 0 && tn)
+ {
+ tn = tn->internal[(chunkId >>(/* dev->chunkGroupBits + */ YAFFS_TNODES_LEVEL0_BITS + (level-1) * YAFFS_TNODES_INTERNAL_BITS)) &
+ YAFFS_TNODES_INTERNAL_MASK];
+ level--;
+
+ }
+
+ return tn;
+}
+
+// AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.
+// This happens in two steps:
+// 1. If the tree isn't tall enough, then make it taller.
+// 2. Scan down the tree towards the level 0 tnode adding tnodes if required.
+//
+// Used when modifying the tree.
+//
+static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId)
+{
+
+ yaffs_Tnode *tn;
+
+ int requiredTallness;
+
+ __u32 i;
+ __u32 l;
+
+
+ //T((TSTR("AddOrFind topLevel=%d, chunk=%d"),fStruct->topLevel,chunkId));
+
+ // Check sane level and page Id
+ if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL)
+ {
+ char str[50];
+ sprintf(str,"Bad level %d",fStruct->topLevel);
+ YALERT(str);
+ return NULL;
+ }
+
+ if(chunkId > YAFFS_MAX_CHUNK_ID)
+ {
+ char str[50];
+ sprintf(str,"Bad chunkId %d",chunkId);
+ YALERT(str);
+ return NULL;
+ }
+
+ // First check we're tall enough (ie enough topLevel)
+
+ i = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);
+ requiredTallness = 0;
+ while(i)
+ {
+ i >>= YAFFS_TNODES_INTERNAL_BITS;
+ requiredTallness++;
+ }
+
+ //T((TSTR(" required=%d"),requiredTallness));
+
+
+ if(requiredTallness > fStruct->topLevel)
+ {
+ // Not tall enough,gotta make the tree taller
+ for(i = fStruct->topLevel; i < requiredTallness; i++)
+ {
+ //T((TSTR(" add new top")));
+
+ tn = yaffs_GetTnode(dev);
+
+ if(tn)
+ {
+ tn->internal[0] = fStruct->top;
+ fStruct->top = tn;
+ }
+ else
+ {
+ YALERT("No more tnodes");
+ }
+ }
+
+ fStruct->topLevel = requiredTallness;
+ }
+
+
+ // Traverse down to level 0, adding anything we need
+
+ l = fStruct->topLevel;
+ tn = fStruct->top;
+ while (l > 0 && tn)
+ {
+ i = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) &
+ YAFFS_TNODES_INTERNAL_MASK;
+
+ //T((TSTR(" [%d:%d]"),l,i));
+
+ if(!tn->internal[i])
+ {
+ //T((TSTR(" added")));
+
+ tn->internal[i] = yaffs_GetTnode(dev);
+ }
+
+ tn = tn->internal[i];
+ l--;
+
+ }
+
+ //TSTR(TENDSTR)));
+
+ return tn;
+}
+
+// DeleteWorker scans backwards through the tnode tree and delets all the
+// chunks and tnodes in the file
+
+static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset)
+{
+ int i;
+ int chunkInInode;
+ int theChunk;
+ yaffs_Tags tags;
+ int found;
+ int chunkDeleted;
+
+
+ if(tn)
+ {
+ if(level > 0)
+ {
+
+ for(i = YAFFS_NTNODES_INTERNAL -1; i >= 0; i--)
+ {
+ if(tn->internal[i])
+ {
+ yaffs_DeleteWorker(in,tn->internal[i],level - 1,
+ (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i );
+ yaffs_FreeTnode(in->myDev,tn->internal[i]);
+ tn->internal[i] = NULL;
+ }
+
+ }
+ }
+ else if(level == 0)
+ {
+ for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--)
+ {
+ if(tn->level0[i])
+ {
+ int j;
+
+ chunkInInode = (chunkOffset << YAFFS_TNODES_LEVEL0_BITS ) + i;
+
+ theChunk = tn->level0[i] << in->myDev->chunkGroupBits;
+
+ // Now we need to search for it
+ for(j = 0,found = 0; theChunk && j < in->myDev->chunkGroupSize && !found; j++)
+ {
+ yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,&tags,&chunkDeleted);
+ if(yaffs_TagsMatch(&tags,in->objectId,chunkInInode,chunkDeleted))
+ {
+ // found it;
+ found = 1;
+
+ }
+ else
+ {
+ theChunk++;
+ }
+ }
+
+ if(found)
+ {
+ yaffs_DeleteChunk(in->myDev,theChunk);
+
+ }
+
+ tn->level0[i] = 0;
+ }
+
+ }
+
+ }
+
+ }
+
+}
+
+
+
+
+// Pruning removes any part of the file structure tree that is beyond the
+// bounds of the file (ie that does not point to chunks).
+//
+// A file should only get pruned when its size is reduced.
+//
+// Before pruning, the chunks must be pulled from the tree and the
+// level 0 tnode entries must be zeroed out.
+// Could also use this for file deletion, but that's probably better handled
+// by a special case.
+
+// yaffs_PruneWorker should only be called by yaffs_PruneFileStructure()
+
+static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, __u32 level, int del0)
+{
+ int i;
+ int hasData;
+
+ if(tn)
+ {
+ hasData = 0;
+
+ for(i = 0; i < YAFFS_NTNODES_INTERNAL; i++)
+ {
+ if(tn->internal[i] && level > 0)
+ {
+ tn->internal[i] = yaffs_PruneWorker(dev,tn->internal[i],level - 1, ( i == 0) ? del0 : 1);
+ }
+
+ if(tn->internal[i])
+ {
+ hasData++;
+ }
+ }
+
+ if(hasData == 0 && del0)
+ {
+ // Free and return NULL
+
+ yaffs_FreeTnode(dev,tn);
+ tn = NULL;
+ }
+
+ }
+
+ return tn;
+
+}
+
+static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStruct)
+{
+ int i;
+ int hasData;
+ int done = 0;
+ yaffs_Tnode *tn;
+
+ if(fStruct->topLevel > 0)
+ {
+ fStruct->top = yaffs_PruneWorker(dev,fStruct->top, fStruct->topLevel,0);
+
+ // Now we have a tree with all the non-zero branches NULL but the height
+ // is the same as it was.
+ // Let's see if we can trim internal tnodes to shorten the tree.
+ // We can do this if only the 0th element in the tnode is in use
+ // (ie all the non-zero are NULL)
+
+ while(fStruct->topLevel && !done)
+ {
+ tn = fStruct->top;
+
+ hasData = 0;
+ for(i = 1; i internal[i])
+ {
+ hasData++;
+ }
+ }
+
+ if(!hasData)
+ {
+ fStruct->top = tn->internal[0];
+ fStruct->topLevel--;
+ yaffs_FreeTnode(dev,tn);
+ }
+ else
+ {
+ done = 1;
+ }
+ }
+ }
+
+ return YAFFS_OK;
+}
+
+
+
+
+/////////////////////// End of File Structure functions. /////////////////
+
+// yaffs_CreateFreeObjects creates a bunch more objects and
+// adds them to the object free list.
+static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)
+{
+ int i;
+ yaffs_Object *newObjects;
+ yaffs_ObjectList *list;
+
+ if(nObjects < 1) return YAFFS_OK;
+
+ // make these things
+
+ newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));
+
+ if (!newObjects)
+ {
+ YALERT("Could not allocate more objects");
+ return YAFFS_FAIL;
+ }
+
+ // Hook them into the free list
+ for(i = 0; i < nObjects - 1; i++)
+ {
+ (yaffs_Object *)newObjects[i].siblings.next = &newObjects[i+1];
+ }
+
+ newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
+ dev->freeObjects = newObjects;
+ dev->nFreeObjects+= nObjects;
+ dev->nObjectsCreated+= nObjects;
+
+ // Now add this bunch of Objects to a list for freeing up.
+
+ list = YMALLOC(sizeof(yaffs_ObjectList));
+ if(!list)
+ {
+ YALERT("Could not add Objects to management list");
+ }
+ else
+ {
+ list->objects = newObjects;
+ list->next = dev->allocatedObjectList;
+ dev->allocatedObjectList = list;
+ }
+
+
+ YINFO("Objects created");
+
+
+ return YAFFS_OK;
+}
+
+
+// AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out
+static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)
+{
+ yaffs_Object *tn = NULL;
+
+ // If there are none left make more
+ if(!dev->freeObjects)
+ {
+ yaffs_CreateFreeObjects(dev,YAFFS_ALLOCATION_NOBJECTS);
+ }
+
+ if(dev->freeObjects)
+ {
+ tn = dev->freeObjects;
+ dev->freeObjects = (yaffs_Object *)(dev->freeObjects->siblings.next);
+ dev->nFreeObjects--;
+
+ // Now sweeten it up...
+
+ memset(tn,0,sizeof(yaffs_Object));
+ tn->chunkId = -1;
+ tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
+ INIT_LIST_HEAD(&(tn->hardLinks));
+ INIT_LIST_HEAD(&(tn->hashLink));
+ INIT_LIST_HEAD(&tn->siblings);
+
+ // Add it to the lost and found directory.
+ // NB Can't put root or lostNFound in lostNFound so
+ // check if lostNFound exists first
+ if(dev->lostNFoundDir)
+ {
+ yaffs_AddObjectToDirectory(dev->lostNFoundDir,tn);
+ }
+ }
+
+
+ return tn;
+}
+
+static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u32 mode)
+{
+
+ yaffs_Object *obj = yaffs_CreateNewObject(dev,number,YAFFS_OBJECT_TYPE_DIRECTORY);
+ if(obj)
+ {
+ obj->fake = 1; // it is fake so it has no NAND presence...
+ obj->renameAllowed= 0; // ... and we're not allowed to rename it...
+ obj->unlinkAllowed= 0; // ... or unlink it
+ obj->st_mode = mode;
+ obj->myDev = dev;
+ obj->chunkId = 0; // Not a valid chunk.
+ }
+
+ return obj;
+
+}
+
+
+static void yaffs_UnhashObject(yaffs_Object *tn)
+{
+ int bucket;
+ yaffs_Device *dev = tn->myDev;
+
+
+ // If it is still linked into the bucket list, free from the list
+ if(!list_empty(&tn->hashLink))
+ {
+ list_del_init(&tn->hashLink);
+ bucket = yaffs_HashFunction(tn->objectId);
+ dev->objectBucket[bucket].count--;
+ }
+
+}
+
+
+// FreeObject frees up a Object and puts it back on the free list
+static void yaffs_FreeObject(yaffs_Object *tn)
+{
+
+ yaffs_Device *dev = tn->myDev;
+
+ yaffs_UnhashObject(tn);
+
+ // Link into the free list.
+ (yaffs_Object *)(tn->siblings.next) = dev->freeObjects;
+ dev->freeObjects = tn;
+ dev->nFreeObjects++;
+}
+
+
+
+
+static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
+{
+ // Free the list of allocated Objects
+
+ while( dev->allocatedObjectList)
+ {
+ YFREE(dev->allocatedObjectList->objects);
+ dev->allocatedObjectList = dev->allocatedObjectList->next;
+ }
+
+ dev->freeObjects = NULL;
+ dev->nFreeObjects = 0;
+}
+
+static void yaffs_InitialiseObjects(yaffs_Device *dev)
+{
+ int i;
+
+ dev->allocatedObjectList = NULL;
+ dev->freeObjects = NULL;
+ dev->nFreeObjects = 0;
+
+ for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++)
+ {
+ INIT_LIST_HEAD(&dev->objectBucket[i].list);
+ dev->objectBucket[i].count = 0;
+ }
+
+}
+
+
+
+
+
+
+int yaffs_FindNiceObjectBucket(yaffs_Device *dev)
+{
+ static int x = 0;
+ int i;
+ int l = 999;
+ int lowest = 999999;
+
+
+ // First let's see if we can find one that's empty.
+
+ for(i = 0; i < 10 && lowest > 0; i++)
+ {
+ x++;
+ x %= YAFFS_NOBJECT_BUCKETS;
+ if(dev->objectBucket[x].count < lowest)
+ {
+ lowest = dev->objectBucket[x].count;
+ l = x;
+ }
+
+ }
+
+ // If we didn't find an empty list, then try
+ // looking a bit further for a short one
+
+ for(i = 0; i < 10 && lowest > 3; i++)
+ {
+ x++;
+ x %= YAFFS_NOBJECT_BUCKETS;
+ if(dev->objectBucket[x].count < lowest)
+ {
+ lowest = dev->objectBucket[x].count;
+ l = x;
+ }
+
+ }
+
+ return l;
+}
+
+static int yaffs_CreateNewObjectNumber(yaffs_Device *dev)
+{
+ int bucket = yaffs_FindNiceObjectBucket(dev);
+
+ // Now find an object value that has not already been taken
+ // by scanning the list.
+
+ int found = 0;
+ struct list_head *i;
+
+ int n = bucket;
+
+ //yaffs_CheckObjectHashSanity();
+
+ while(!found)
+ {
+ found = 1;
+ n += YAFFS_NOBJECT_BUCKETS;
+ if(1 ||dev->objectBucket[bucket].count > 0)
+ {
+ list_for_each(i,&dev->objectBucket[bucket].list)
+ {
+ // If there is already one in the list
+ if(list_entry(i, yaffs_Object,hashLink)->objectId == n)
+ {
+ found = 0;
+ }
+ }
+ }
+ }
+
+ //T(("bucket %d count %d inode %d\n",bucket,yaffs_objectBucket[bucket].count,n);
+
+ return n;
+}
+
+void yaffs_HashObject(yaffs_Object *in)
+{
+ int bucket = yaffs_HashFunction(in->objectId);
+ yaffs_Device *dev = in->myDev;
+
+ if(!list_empty(&in->hashLink))
+ {
+ YINFO("!!!");
+ }
+
+
+ list_add(&in->hashLink,&dev->objectBucket[bucket].list);
+ dev->objectBucket[bucket].count++;
+
+}
+
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number)
+{
+ int bucket = yaffs_HashFunction(number);
+ struct list_head *i;
+ yaffs_Object *in;
+
+ list_for_each(i,&dev->objectBucket[bucket].list)
+ {
+ // Look if it is in the list
+ in = list_entry(i, yaffs_Object,hashLink);
+ if(in->objectId == number)
+ {
+ return in;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type)
+{
+
+ yaffs_Object *theObject;
+
+ if(number < 0)
+ {
+ number = yaffs_CreateNewObjectNumber(dev);
+ }
+
+ theObject = yaffs_AllocateEmptyObject(dev);
+
+ if(theObject)
+ {
+ theObject->fake = 0;
+ theObject->renameAllowed = 1;
+ theObject->unlinkAllowed = 1;
+ theObject->objectId = number;
+ theObject->myDev = dev;
+ yaffs_HashObject(theObject);
+ theObject->variantType = type;
+ theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME;
+
+ switch(type)
+ {
+ case YAFFS_OBJECT_TYPE_FILE:
+ theObject->variant.fileVariant.fileSize = 0;
+ theObject->variant.fileVariant.scannedFileSize = 0;
+ theObject->variant.fileVariant.topLevel = 0;
+ theObject->variant.fileVariant.top = yaffs_GetTnode(dev);
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ INIT_LIST_HEAD(&theObject->variant.directoryVariant.children);
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ // No action required
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ // No action required
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ // No action required
+ break;
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ // todo this should not happen
+ break;
+ }
+ }
+
+ return theObject;
+}
+
+yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type)
+{
+ yaffs_Object *theObject = NULL;
+
+ if(number > 0)
+ {
+ theObject = yaffs_FindObjectByNumber(dev,number);
+ }
+
+ if(!theObject)
+ {
+ theObject = yaffs_CreateNewObject(dev,number,type);
+ }
+
+ return theObject;
+
+}
+
+char *yaffs_CloneString(const char *str)
+{
+ char *newStr = NULL;
+
+ if(str && *str)
+ {
+ newStr = YMALLOC(strlen(str) + 1);
+ strcpy(newStr,str);
+ }
+
+ return newStr;
+
+}
+
+//
+// Mknod (create) a new object.
+// equivalentObject only has meaning for a hard link;
+// aliasString only has meaning for a sumlink.
+// rdev only has meaning for devices (a subset of special objects)
+yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,
+ yaffs_Object *parent,
+ const char *name,
+ __u32 mode,
+ __u32 uid,
+ __u32 gid,
+ yaffs_Object *equivalentObject,
+ const char *aliasString,
+ __u32 rdev)
+{
+ yaffs_Object *in;
+
+ yaffs_Device *dev = parent->myDev;
+
+ // Check if the entry exists. If it does then fail the call since we don't want a dup.
+ if(yaffs_FindObjectByName(parent,name))
+ {
+ return NULL;
+ }
+
+ in = yaffs_CreateNewObject(dev,-1,type);
+
+ if(in)
+ {
+ in->chunkId = -1;
+ in->valid = 1;
+ in->variantType = type;
+
+ in->st_mode = mode;
+ in->st_rdev = rdev;
+ in->st_uid = uid;
+ in->st_gid = gid;
+ in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME;
+
+ in->nDataChunks = 0;
+
+ in->sum = yaffs_CalcNameSum(name);
+ in->dirty = 1;
+
+ yaffs_AddObjectToDirectory(parent,in);
+
+ in->myDev = parent->myDev;
+
+
+ switch(type)
+ {
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ in->variant.symLinkVariant.alias = yaffs_CloneString(aliasString);
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ in->variant.hardLinkVariant.equivalentObject = equivalentObject;
+ in->variant.hardLinkVariant.equivalentObjectId = equivalentObject->objectId;
+ list_add(&in->hardLinks,&equivalentObject->hardLinks);
+ break;
+ case YAFFS_OBJECT_TYPE_FILE: // do nothing
+ case YAFFS_OBJECT_TYPE_DIRECTORY: // do nothing
+ case YAFFS_OBJECT_TYPE_SPECIAL: // do nothing
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ break;
+ }
+
+ yaffs_UpdateObjectHeader(in,name);
+
+ }
+
+ return in;
+}
+
+yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)
+{
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE,parent,name,mode,uid,gid,NULL,NULL,0);
+}
+
+yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)
+{
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,0);
+}
+
+yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev)
+{
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,rdev);
+}
+
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,const char *alias)
+{
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK,parent,name,mode,uid,gid,NULL,alias,0);
+}
+
+// NB yaffs_Link returns the object id of the equivalent object.
+yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject)
+{
+ // Get the real object in case we were fed a hard link as an equivalent object
+ equivalentObject = yaffs_GetEquivalentObject(equivalentObject);
+
+ if(yaffs_MknodObject(YAFFS_OBJECT_TYPE_HARDLINK,parent,name,0,0,0,equivalentObject,NULL,0))
+ {
+ return equivalentObject;
+ }
+ else
+ {
+ return NULL;
+ }
+
+}
+
+
+static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName)
+{
+ //yaffs_Device *dev = obj->myDev;
+
+ if(newDir == NULL)
+ {
+ newDir = obj->parent; // use the old directory
+ }
+
+ // Only proceed if the new name does not exist and
+ // if we're putting it into a directory.
+ if(!yaffs_FindObjectByName(newDir,newName) &&
+ newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
+ {
+ obj->sum = yaffs_CalcNameSum(newName);
+ obj->dirty = 1;
+ yaffs_AddObjectToDirectory(newDir,obj);
+
+ if(yaffs_UpdateObjectHeader(obj,newName) >= 0)
+ {
+ return YAFFS_OK;
+ }
+ }
+
+ return YAFFS_FAIL;
+}
+
+int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName)
+{
+ yaffs_Object *obj;
+
+ obj = yaffs_FindObjectByName(oldDir,oldName);
+ if(obj && obj->renameAllowed)
+ {
+ return yaffs_ChangeObjectName(obj,newDir,newName);
+ }
+ return YAFFS_FAIL;
+}
+
+
+
+static int yaffs_CheckObjectHashSanity(yaffs_Device *dev)
+{
+ // Scan the buckets and check that the lists
+ // have as many members as the count says there are
+ int bucket;
+ int countEm;
+ struct list_head *j;
+ int ok = YAFFS_OK;
+
+ for(bucket = 0; bucket < YAFFS_NOBJECT_BUCKETS; bucket++)
+ {
+ countEm = 0;
+
+ list_for_each(j,&dev->objectBucket[bucket].list)
+ {
+ countEm++;
+ }
+
+ if(countEm != dev->objectBucket[bucket].count)
+ {
+ YALERT("Inode hash inconsistency");
+ ok = YAFFS_FAIL;
+ }
+ }
+
+ return ok;
+}
+
+void yaffs_ObjectTest(yaffs_Device *dev)
+{
+ yaffs_Object *in[1000];
+ int inNo[1000];
+ yaffs_Object *inold[1000];
+ int i;
+ int j;
+
+ memset(in,0,1000*sizeof(yaffs_Object *));
+ memset(inold,0,1000*sizeof(yaffs_Object *));
+
+ yaffs_CheckObjectHashSanity(dev);
+
+ for(j = 0; j < 10; j++)
+ {
+ //T(("%d\n",j));
+
+ for(i = 0; i < 1000; i++)
+ {
+ in[i] = yaffs_CreateNewObject(dev,-1,YAFFS_OBJECT_TYPE_FILE);
+ if(!in[i])
+ {
+ YINFO("No more inodes");
+ }
+ else
+ {
+ inNo[i] = in[i]->objectId;
+ }
+ }
+
+ for(i = 0; i < 1000; i++)
+ {
+ if(yaffs_FindObjectByNumber(dev,inNo[i]) != in[i])
+ {
+ //T(("Differnce in look up test\n"));
+ }
+ else
+ {
+ // T(("Look up ok\n"));
+ }
+ }
+
+ yaffs_CheckObjectHashSanity(dev);
+
+ for(i = 0; i < 1000; i+=3)
+ {
+ yaffs_FreeObject(in[i]);
+ in[i] = NULL;
+ }
+
+
+ yaffs_CheckObjectHashSanity(dev);
+ }
+
+}
+
+
+
+/////////////////////////// Block Management and Page Allocation ///////////////////
+
+
+static void yaffs_InitialiseBlocks(yaffs_Device *dev)
+{
+ dev->blockInfo = YMALLOC(dev->nBlocks * sizeof(yaffs_BlockInfo));
+ memset(dev->blockInfo,0,dev->nBlocks * sizeof(yaffs_BlockInfo));
+ dev->allocationBlock = -1; // force it to get a new one
+}
+
+static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
+{
+ YFREE(dev->blockInfo);
+}
+
+// FindDiretiestBlock is used to select the dirtiest block (or close enough)
+// for garbage collection.
+
+static int yaffs_FindDirtiestBlock(yaffs_Device *dev)
+{
+
+ int b = dev->currentDirtyChecker;
+
+ int i;
+ int dirtiest = -1;
+ int pagesInUse = 100; // silly big number
+
+ for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)
+ {
+ b++;
+ if (b > dev->endBlock)
+ {
+ b = dev->startBlock;
+ }
+
+ if(dev->blockInfo[b].blockState == YAFFS_BLOCK_STATE_FULL &&
+ (dev->blockInfo)[b].pagesInUse < pagesInUse)
+ {
+ dirtiest = b;
+ pagesInUse = (dev->blockInfo)[b].pagesInUse;
+ }
+ }
+
+ dev->currentDirtyChecker = b;
+
+ return dirtiest;
+}
+
+
+static int yaffs_FindBlockForAllocation(yaffs_Device *dev,int useReserve)
+{
+ int i;
+
+ if(useReserve && dev->nErasedBlocks < 1)
+ {
+ // Hoosterman we've got a problem.
+ // Can't get space to gc
+ return -1;
+ }
+ else if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)
+ {
+ // We are not in GC, so we hold some in reserve so we can get
+ // a gc done.
+ }
+
+ // Find an empty block.
+
+ for(i = dev->startBlock; i <= dev->endBlock; i++)
+ {
+
+ if(dev->blockInfo[i].blockState == YAFFS_BLOCK_STATE_EMPTY)
+ {
+ dev->blockInfo[i].blockState = YAFFS_BLOCK_STATE_ALLOCATING;
+ dev->nErasedBlocks--;
+ if(dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)
+ {
+ dev->garbageCollectionRequired = 1;
+ }
+
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)
+{
+ yaffs_BlockInfo *bi = &dev->blockInfo[blockNo];
+
+ // Mark as dirty, erase it and mark as clean.
+ bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
+ yaffs_EraseBlockInNAND(dev,blockNo);
+ bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
+ dev->nErasedBlocks++;
+ bi->pagesInUse = 0;
+ bi->pageBits = 0;
+
+ T((TSTR("Erased block %d" TENDSTR),blockNo));
+}
+
+
+static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
+{
+ int retVal;
+
+ if(dev->allocationBlock < 0)
+ {
+ // Get next block to allocate off
+ dev->allocationBlock = yaffs_FindBlockForAllocation(dev,useReserve);
+ dev->allocationPage = 0;
+ }
+
+ // Next page please....
+ if(dev->allocationBlock >= 0)
+ {
+ retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) +
+ dev->allocationPage;
+ dev->blockInfo[dev->allocationBlock].pagesInUse++;
+ dev->blockInfo[dev->allocationBlock].pageBits |=
+ (1 << (dev->allocationPage));
+
+ dev->allocationPage++;
+
+ dev->nFreeChunks--;
+
+ // If the block is full set the state to full
+ if(dev->allocationPage >= YAFFS_CHUNKS_PER_BLOCK)
+ {
+ dev->blockInfo[dev->allocationBlock].blockState = YAFFS_BLOCK_STATE_FULL;
+ dev->allocationBlock = -1;
+ }
+
+#ifdef YAFFS_PARANOID
+ if(yaffs_CheckChunkErased(dev,retVal) == YAFFS_FAIL)
+ {
+ T((TSTR("..................Trying to allocate non-erased page %d" TENDSTR),retVal));
+ }
+#endif
+ return retVal;
+
+ }
+ T((TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
+
+ return -1;
+}
+
+
+int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
+{
+ int oldChunk;
+ int newChunk;
+ __u32 mask;
+
+
+ yaffs_Spare spare;
+ yaffs_Tags tags;
+ __u8 buffer[YAFFS_BYTES_PER_CHUNK];
+
+ yaffs_BlockInfo *bi = &dev->blockInfo[block];
+
+ yaffs_Object *object;
+
+ //T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));
+
+ for(mask = 1,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK;
+ mask && bi->pageBits;
+ mask <<= 1, oldChunk++ )
+ {
+ if(bi->pageBits & mask)
+ {
+
+ // This page is in use and needs to be copied off
+
+ dev->nGCCopies++;
+
+ //T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));
+
+ yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare);
+
+ yaffs_GetTagsFromSpare(&spare,&tags);
+ tags.serialNumber++;
+ yaffs_LoadTagsIntoSpare(&spare,&tags);
+
+#if 0
+ newChunk = yaffs_AllocatePage(dev,1);
+ if(newChunk < 0)
+ {
+ return YAFFS_FAIL;
+ }
+
+ yaffs_WriteChunkToNAND(dev,newChunk, buffer, &spare);
+
+#else
+ newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);
+#endif
+ if(newChunk < 0)
+ {
+ return YAFFS_FAIL;
+ }
+
+ object = yaffs_FindObjectByNumber(dev,tags.objectId);
+
+ // Ok, now fix up the Tnodes etc.
+
+ if(tags.chunkId == 0)
+ {
+ // It's a header
+ object->chunkId = newChunk;
+ }
+ else
+ {
+ // It's a data chunk
+ yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
+
+ }
+
+ yaffs_DeleteChunk(dev,oldChunk);
+
+ }
+ }
+
+ return YAFFS_OK;
+}
+
+int yaffs_CheckGarbageCollection(yaffs_Device *dev)
+{
+ int block;
+
+ if(dev->garbageCollectionRequired)
+ {
+ dev->garbageCollectionRequired = 0;
+ block = yaffs_FindDirtiestBlock(dev);
+ if(block >= 0)
+ {
+ return yaffs_GarbageCollectBlock(dev,block);
+ }
+ else
+ {
+ return YAFFS_FAIL;
+ }
+ }
+
+ return YAFFS_OK;
+}
+
+
+//////////////////////////// TAGS ///////////////////////////////////////
+
+static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr)
+{
+ yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
+
+ yaffs_CalcTagsECC(tagsPtr);
+
+ sparePtr->tagByte0 = tu->asBytes[0];
+ sparePtr->tagByte1 = tu->asBytes[1];
+ sparePtr->tagByte2 = tu->asBytes[2];
+ sparePtr->tagByte3 = tu->asBytes[3];
+ sparePtr->tagByte4 = tu->asBytes[4];
+ sparePtr->tagByte5 = tu->asBytes[5];
+ sparePtr->tagByte6 = tu->asBytes[6];
+ sparePtr->tagByte7 = tu->asBytes[7];
+}
+
+static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)
+{
+ yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
+
+ tu->asBytes[0]= sparePtr->tagByte0;
+ tu->asBytes[1]= sparePtr->tagByte1;
+ tu->asBytes[2]= sparePtr->tagByte2;
+ tu->asBytes[3]= sparePtr->tagByte3;
+ tu->asBytes[4]= sparePtr->tagByte4;
+ tu->asBytes[5]= sparePtr->tagByte5;
+ tu->asBytes[6]= sparePtr->tagByte6;
+ tu->asBytes[7]= sparePtr->tagByte7;
+
+ yaffs_CheckECCOnTags(tagsPtr);
+}
+
+static void yaffs_SpareInitialise(yaffs_Spare *spare)
+{
+ memset(spare,0xFF,sizeof(yaffs_Spare));
+}
+
+static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted)
+{
+ if(tags)
+ {
+ yaffs_Spare spare;
+ if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare) == YAFFS_OK)
+ {
+ *chunkDeleted = (yaffs_countBits[spare.pageStatus] < 7) ? 1 : 0;
+ yaffs_GetTagsFromSpare(&spare,tags);
+ return YAFFS_OK;
+ }
+ else
+ {
+ return YAFFS_FAIL;
+ }
+ }
+
+ return YAFFS_OK;
+}
+
+#if 0
+static int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_Tags *tags)
+{
+ // NB There must be tags, data is optional
+ // If there is data, then an ECC is calculated on it.
+
+ yaffs_Spare spare;
+
+ if(!tags)
+ {
+ return YAFFS_FAIL;
+ }
+
+ yaffs_SpareInitialise(&spare);
+
+
+ if(buffer)
+ {
+ yaffs_CalcECC(buffer,&spare);
+ }
+
+ yaffs_LoadTagsIntoSpare(&spare,tags);
+
+ return yaffs_WriteChunkToNAND(dev,chunkInNAND,buffer,&spare);
+
+}
+#endif
+
+
+static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_Tags *tags, int useReserve)
+{
+ // NB There must be tags, data is optional
+ // If there is data, then an ECC is calculated on it.
+
+ yaffs_Spare spare;
+
+ if(!tags)
+ {
+ return YAFFS_FAIL;
+ }
+
+ yaffs_SpareInitialise(&spare);
+
+
+ if(buffer)
+ {
+ yaffs_CalcECC(buffer,&spare);
+ }
+
+ yaffs_LoadTagsIntoSpare(&spare,tags);
+
+ return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve);
+
+}
+
+static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted)
+{
+ return ( tags->chunkId == chunkInObject &&
+ tags->objectId == objectId &&
+ !chunkDeleted) ? 1 : 0;
+
+}
+
+
+
+int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
+{
+ //Get the Tnode, then get the level 0 offset chunk offset
+ yaffs_Tnode *tn;
+ int theChunk = -1;
+ yaffs_Tags localTags;
+ int i;
+ int found = 0;
+ int chunkDeleted;
+
+ yaffs_Device *dev = in->myDev;
+
+
+ if(!tags)
+ {
+ // Passed a NULL, so use our own tags space
+ tags = &localTags;
+ }
+
+ tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
+
+ if(tn)
+ {
+ theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
+
+ // Now we need to do the shifting etc and search for it
+ for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
+ {
+ yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted);
+ if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted))
+ {
+ // found it;
+ found = 1;
+ }
+ else
+ {
+ theChunk++;
+ }
+ }
+ }
+ return found ? theChunk : -1;
+}
+
+int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
+{
+ //Get the Tnode, then get the level 0 offset chunk offset
+ yaffs_Tnode *tn;
+ int theChunk = -1;
+ yaffs_Tags localTags;
+ int i;
+ int found = 0;
+ yaffs_Device *dev = in->myDev;
+ int chunkDeleted;
+
+ if(!tags)
+ {
+ // Passed a NULL, so use our own tags space
+ tags = &localTags;
+ }
+
+ tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
+
+ if(tn)
+ {
+
+ theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
+
+ // Now we need to do the shifting etc and search for it
+ for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
+ {
+ yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted);
+ if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted))
+ {
+ // found it;
+ found = 1;
+ }
+ else
+ {
+ theChunk++;
+ }
+ }
+
+ // Delete the entry in the filestructure
+ if(found)
+ {
+ tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0;
+ }
+ }
+ else
+ {
+ //T(("No level 0 found for %d\n", chunkInInode));
+ }
+
+ if(!found)
+ {
+ //T(("Could not find %d to delete\n",chunkInInode));
+ }
+ return found ? theChunk : -1;
+}
+
+
+#if YAFFS_PARANOID
+
+static int yaffs_CheckFileSanity(yaffs_Object *in)
+{
+ int chunk;
+ int nChunks;
+ int fSize;
+ int failed = 0;
+ int objId;
+ yaffs_Tnode *tn;
+ yaffs_Tags localTags;
+ yaffs_Tags *tags = &localTags;
+ int theChunk;
+ int chunkDeleted;
+
+
+ if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
+ {
+ //T(("Object not a file\n"));
+ return YAFFS_FAIL;
+ }
+
+ objId = in->objectId;
+ fSize = in->variant.fileVariant.fileSize;
+ nChunks = (fSize + YAFFS_BYTES_PER_CHUNK -1)/YAFFS_BYTES_PER_CHUNK;
+
+ for(chunk = 1; chunk <= nChunks; chunk++)
+ {
+ tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk);
+
+ if(tn)
+ {
+
+ theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits;
+
+
+ yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags,&chunkDeleted);
+ if(yaffs_TagsMatch(tags,in->objectId,chunk,chunkDeleted))
+ {
+ // found it;
+
+ }
+ else
+ {
+ //T(("File problem file [%d,%d] NAND %d tags[%d,%d]\n",
+ // objId,chunk,theChunk,tags->chunkId,tags->objectId);
+
+ failed = 1;
+
+ }
+
+ }
+ else
+ {
+ //T(("No level 0 found for %d\n", chunk));
+ }
+ }
+
+ return failed ? YAFFS_FAIL : YAFFS_OK;
+}
+
+#endif
+
+static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan)
+{
+ yaffs_Tnode *tn;
+ yaffs_Device *dev = in->myDev;
+ int existingChunk;
+ yaffs_Tags existingTags;
+ yaffs_Tags newTags;
+ unsigned existingSerial, newSerial;
+
+ int newChunkDeleted;
+
+
+ tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
+ if(!tn)
+ {
+ return YAFFS_FAIL;
+ }
+
+ existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK];
+
+ if(inScan)
+ {
+ // If we're scanning then we need to test for duplicates
+ // NB This does not need to be efficient since it should only ever
+ // happen when the power fails during a write, then only one
+ // chunk should ever be affected.
+
+
+ if(existingChunk != 0)
+ {
+ // NB Right now existing chunk will not be real chunkId if the device >= 32MB
+ // thus we have to do a FindChunkInFile to get the real chunk id.
+ //
+ // We have a duplicate now we need to decide which one to use
+ // To do this we get both sets of tags and compare serial numbers.
+ yaffs_ReadChunkTagsFromNAND(dev,chunkInNAND, &newTags,&newChunkDeleted);
+
+
+ // Do a proper find
+ existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags);
+
+ if(existingChunk <=0)
+ {
+ //Hoosterman - how did this happen?
+ // todo
+ }
+
+
+ // NB The deleted flags should be false, otherwise the chunks will
+ // not be loaded during a scan
+
+ newSerial = newTags.serialNumber;
+ existingSerial = existingTags.serialNumber;
+
+ if( existingChunk <= 0 ||
+ ((existingSerial+1) & 3) == newSerial)
+ {
+ // Use new
+ // Delete the old one and drop through to update the tnode
+ yaffs_DeleteChunk(dev,existingChunk);
+ }
+ else
+ {
+ // Use existing.
+ // Delete the new one and return early so that the tnode isn't changed
+ yaffs_DeleteChunk(dev,chunkInNAND);
+ return YAFFS_OK;
+ }
+ }
+
+ }
+
+ if(existingChunk == 0)
+ {
+ in->nDataChunks++;
+ }
+
+ tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = (chunkInNAND >> dev->chunkGroupBits);
+
+ return YAFFS_OK;
+}
+
+
+
+int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)
+{
+ int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL);
+
+ if(chunkInNAND >= 0)
+ {
+ return yaffs_ReadChunkFromNAND(in->myDev,chunkInNAND,buffer,NULL);
+ }
+ else
+ {
+ return 0;
+ }
+
+}
+
+
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId)
+{
+ int block = chunkId / YAFFS_CHUNKS_PER_BLOCK;
+ int page = chunkId % YAFFS_CHUNKS_PER_BLOCK;
+ yaffs_Spare spare;
+
+ yaffs_SpareInitialise(&spare);
+
+ spare.pageStatus = 0; // To mark it as deleted.
+
+
+ yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);
+
+
+ // Pull out of the management area.
+ // If the whole block became dirty, this will kick off an erasure.
+ if( dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
+ dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL)
+ {
+ dev->nFreeChunks++;
+
+ dev->blockInfo[block].pageBits &= ~(1 << page);
+ dev->blockInfo[block].pagesInUse--;
+
+ if( dev->blockInfo[block].pagesInUse == 0 &&
+ dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL)
+ {
+ yaffs_BlockBecameDirty(dev,block);
+ }
+
+ }
+ else
+ {
+ // T(("Bad news deleting chunk %d\n",chunkId));
+ }
+
+}
+
+
+
+
+int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve)
+{
+ // Find old chunk Need to do this to get serial number
+ // Write new one and patch into tree.
+ // Invalidate old tags.
+
+ int prevChunkId;
+ yaffs_Tags prevTags;
+
+ int newChunkId;
+ yaffs_Tags newTags;
+
+ yaffs_Device *dev = in->myDev;
+
+ yaffs_CheckGarbageCollection(dev);
+
+ // Get the previous chunk at this location in the file if it exists
+ prevChunkId = yaffs_FindChunkInFile(in,chunkInInode,&prevTags);
+
+ // Set up new tags
+ newTags.chunkId = chunkInInode;
+ newTags.objectId = in->objectId;
+ newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;
+ newTags.byteCount = nBytes;
+ newTags.unusedStuff = 0xFFFFFFFF;
+
+ yaffs_CalcTagsECC(&newTags);
+
+
+ #if 0
+ // Create new chunk in NAND
+ newChunkId = yaffs_AllocatePage(dev,useReserve);
+
+
+ if(newChunkId >= 0)
+ {
+
+
+ yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,buffer,&newTags);
+
+ yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId);
+
+
+ if(prevChunkId >= 0)
+ {
+ yaffs_DeleteChunk(dev,prevChunkId);
+
+ }
+
+ yaffs_CheckFileSanity(in);
+
+ return newChunkId;
+ }
+
+
+ return -1;
+#else
+
+ newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);
+ if(newChunkId >= 0)
+ {
+ yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);
+
+
+ if(prevChunkId >= 0)
+ {
+ yaffs_DeleteChunk(dev,prevChunkId);
+
+ }
+
+ yaffs_CheckFileSanity(in);
+ }
+ return newChunkId;
+
+#endif
+
+
+
+}
+
+
+// UpdateObjectHeader updates the header on NAND for an object.
+// If name is not NULL, then that new name is used.
+//
+int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name)
+{
+
+ yaffs_Device *dev = in->myDev;
+
+ int prevChunkId;
+
+ int newChunkId;
+ yaffs_Tags newTags;
+ __u8 bufferNew[YAFFS_BYTES_PER_CHUNK];
+ __u8 bufferOld[YAFFS_BYTES_PER_CHUNK];
+
+ yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew;
+ yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;
+
+ if(!in->fake)
+ {
+
+ yaffs_CheckGarbageCollection(dev);
+
+ memset(bufferNew,0xFF,YAFFS_BYTES_PER_CHUNK);
+
+ prevChunkId = in->chunkId;
+
+ if(prevChunkId >= 0)
+ {
+ yaffs_ReadChunkFromNAND(dev,prevChunkId,bufferOld,NULL);
+ }
+
+ // Header data
+ oh->type = in->variantType;
+
+ oh->st_mode = in->st_mode;
+ oh->st_uid = in->st_uid;
+ oh->st_gid = in->st_gid;
+ oh->st_atime = in->st_atime;
+ oh->st_mtime = in->st_mtime;
+ oh->st_ctime = in->st_ctime;
+ oh->st_rdev = in->st_rdev;
+
+ oh->parentObjectId = in->parent->objectId;
+ oh->sum = in->sum;
+ if(name && *name)
+ {
+ memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);
+ strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);
+ }
+ else
+ {
+ memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1);
+ }
+
+ switch(in->variantType)
+ {
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ // Should not happen
+ break;
+ case YAFFS_OBJECT_TYPE_FILE:
+ oh->fileSize = in->variant.fileVariant.fileSize;
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId;
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ // Do nothing
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ // Do nothing
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH);
+ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
+ break;
+ }
+
+ // Tags
+ in->serial++;
+ newTags.chunkId = 0;
+ newTags.objectId = in->objectId;
+ newTags.serialNumber = in->serial;
+ newTags.byteCount = 0xFFFFFFFF;
+ newTags.unusedStuff = 0xFFFFFFFF;
+
+ yaffs_CalcTagsECC(&newTags);
+
+
+
+#if 0
+ // Create new chunk in NAND
+ newChunkId = yaffs_AllocatePage(dev,1);
+
+ if(newChunkId >= 0)
+ {
+
+ yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,bufferNew,&newTags);
+
+ in->chunkId = newChunkId;
+
+ if(prevChunkId >= 0)
+ {
+ yaffs_DeleteChunk(dev,prevChunkId);
+ }
+
+ in->dirty = 0;
+ return newChunkId;
+ }
+
+ return -1;
+#else
+ // Create new chunk in NAND
+ newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1);
+
+ if(newChunkId >= 0)
+ {
+
+ in->chunkId = newChunkId;
+
+ if(prevChunkId >= 0)
+ {
+ yaffs_DeleteChunk(dev,prevChunkId);
+ }
+
+ in->dirty = 0;
+ }
+
+ return newChunkId;
+
+#endif
+ }
+ return 0;
+}
+
+
+
+///////////////////////// File read/write ///////////////////////////////
+// Read and write have very similar structures.
+// In general the read/write has three parts to it
+// * An incomplete chunk to start with (if the read/write is not chunk-aligned)
+// * Some complete chunks
+// * An incomplete chunk to end off with
+//
+// Curve-balls: the first chunk might also be the last chunk.
+
+int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes)
+{
+
+// yaffs_Device *dev = in->myDev;
+
+ __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
+
+ int chunk;
+ int start;
+ int nToCopy;
+ int n = nBytes;
+ int nDone = 0;
+
+ while(n > 0)
+ {
+ chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; // The first chunk is 1
+ start = offset % YAFFS_BYTES_PER_CHUNK;
+
+ // OK now check for the curveball where the start and end are in
+ // the same chunk.
+ if( (start + n) < YAFFS_BYTES_PER_CHUNK)
+ {
+ nToCopy = n;
+ }
+ else
+ {
+ nToCopy = YAFFS_BYTES_PER_CHUNK - start;
+ }
+
+ if(nToCopy != YAFFS_BYTES_PER_CHUNK)
+ {
+ // An incomplete start or end chunk (or maybe both start and end chunk)
+ // Read into the local buffer then copy...
+
+ yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
+ memcpy(buffer,&localBuffer[start],nToCopy);
+ }
+ else
+ {
+ // A full chunk. Read directly into the supplied buffer.
+ yaffs_ReadChunkDataFromObject(in,chunk,buffer);
+ }
+
+ n -= nToCopy;
+ offset += nToCopy;
+ buffer += nToCopy;
+ nDone += nToCopy;
+
+ }
+
+ return nDone;
+}
+
+
+int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes)
+{
+ __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
+
+ int chunk;
+ int start;
+ int nToCopy;
+ int n = nBytes;
+ int nDone = 0;
+ int nToWriteBack;
+ int endOfWrite = offset+nBytes;
+ int chunkWritten = 0;
+
+ while(n > 0 && chunkWritten >= 0)
+ {
+ chunk = offset / YAFFS_BYTES_PER_CHUNK + 1;
+ start = offset % YAFFS_BYTES_PER_CHUNK;
+
+
+ // OK now check for the curveball where the start and end are in
+ // the same chunk.
+ if( (start + n) < YAFFS_BYTES_PER_CHUNK)
+ {
+ nToCopy = n;
+ nToWriteBack = (start + n);
+ }
+ else
+ {
+ nToCopy = YAFFS_BYTES_PER_CHUNK - start;
+ nToWriteBack = YAFFS_BYTES_PER_CHUNK;
+ }
+
+ if(nToCopy != YAFFS_BYTES_PER_CHUNK)
+ {
+ // An incomplete start or end chunk (or maybe both start and end chunk)
+ // Read into the local buffer then copy, then copy over and write back.
+
+ yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
+
+ memcpy(&localBuffer[start],buffer,nToCopy);
+
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0);
+
+ //T(("Write with readback to chunk %d %d\n",chunk,chunkWritten));
+
+ }
+ else
+ {
+ // A full chunk. Write directly from the supplied buffer.
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0);
+ //T(("Write to chunk %d %d\n",chunk,chunkWritten));
+ }
+
+ if(chunkWritten >= 0)
+ {
+ n -= nToCopy;
+ offset += nToCopy;
+ buffer += nToCopy;
+ nDone += nToCopy;
+ }
+
+ }
+
+ // Update file object
+
+ if(endOfWrite > in->variant.fileVariant.fileSize)
+ {
+ in->variant.fileVariant.fileSize = endOfWrite;
+ }
+
+ in->dirty = 1;
+ /*in->st_mtime = CURRENT_TIME; only update in flush*/
+
+ return nDone;
+}
+
+
+int yaffs_ResizeFile(yaffs_Object *in, int newSize)
+{
+ int i;
+ int chunkId;
+ int oldFileSize = in->variant.fileVariant.fileSize;
+ int sizeOfPartialChunk = newSize % YAFFS_BYTES_PER_CHUNK;
+
+ yaffs_Device *dev = in->myDev;
+
+ __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
+
+ if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
+ {
+ return yaffs_GetFileSize(in);
+ }
+
+ if(newSize < oldFileSize)
+ {
+
+ int lastDel = 1 + (oldFileSize-1)/YAFFS_BYTES_PER_CHUNK;
+
+ int startDel = 1 + (newSize + YAFFS_BYTES_PER_CHUNK - 1)/
+ YAFFS_BYTES_PER_CHUNK;
+
+ // Delete backwards so that we don't end up with holes if
+ // power is lost part-way through the operation.
+ for(i = lastDel; i >= startDel; i--)
+ {
+ // NB this could be optimised somewhat,
+ // eg. could retrieve the tags and write them without
+ // using yaffs_DeleteChunk
+
+ chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);
+ if(chunkId <= 0 || chunkId >= (dev->endBlock * 32))
+ {
+ //T(("Found daft chunkId %d for %d\n",chunkId,i));
+ }
+ else
+ {
+ in->nDataChunks--;
+ yaffs_DeleteChunk(dev,chunkId);
+ }
+ }
+
+
+ if(sizeOfPartialChunk != 0)
+ {
+ int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK;
+
+ // Got to read and rewrite the last chunk with its new size.
+ yaffs_ReadChunkDataFromObject(in,lastChunk,localBuffer);
+
+ yaffs_WriteChunkDataToObject(in,lastChunk,localBuffer,sizeOfPartialChunk,1);
+
+ }
+
+ in->variant.fileVariant.fileSize = newSize;
+
+ yaffs_PruneFileStructure(dev,&in->variant.fileVariant);
+
+ return newSize;
+
+ }
+ else
+ {
+ return oldFileSize;
+ }
+}
+
+
+loff_t yaffs_GetFileSize(yaffs_Object *obj)
+{
+ obj = yaffs_GetEquivalentObject(obj);
+
+ switch(obj->variantType)
+ {
+ case YAFFS_OBJECT_TYPE_FILE:
+ return obj->variant.fileVariant.fileSize;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ return strlen(obj->variant.symLinkVariant.alias);
+ default:
+ return 0;
+ }
+}
+
+
+
+// yaffs_FlushFile() updates the file's
+// objectId in NAND
+
+int yaffs_FlushFile(yaffs_Object *in)
+{
+ int retVal;
+ if(in->dirty)
+ {
+ //T(("flushing object header\n"));
+
+ in->st_mtime = CURRENT_TIME;
+
+ retVal = yaffs_UpdateObjectHeader(in,NULL);
+ }
+ else
+ {
+ retVal = YAFFS_OK;
+ }
+
+ return retVal;
+
+}
+
+
+static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)
+{
+ yaffs_RemoveObjectFromDirectory(in);
+ yaffs_DeleteChunk(in->myDev,in->chunkId);
+ yaffs_FreeObject(in);
+ return YAFFS_OK;
+
+}
+
+// yaffs_DeleteFile deletes the whole file data
+// and the inode associated with the file.
+// It does not delete the links associated with the file.
+static int yaffs_DeleteFile(yaffs_Object *in)
+{
+ // Delete the file data & tnodes
+#if 0
+ yaffs_ResizeFile(in,0);
+#else
+ yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0);
+
+#endif
+
+ yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
+
+ return yaffs_DoGenericObjectDeletion(in);
+}
+
+static int yaffs_DeleteDirectory(yaffs_Object *in)
+{
+ //First check that the directory is empty.
+ if(list_empty(&in->variant.directoryVariant.children))
+ {
+ return yaffs_DoGenericObjectDeletion(in);
+ }
+
+ return YAFFS_FAIL;
+
+}
+
+static int yaffs_DeleteSymLink(yaffs_Object *in)
+{
+ YFREE(in->variant.symLinkVariant.alias);
+
+ return yaffs_DoGenericObjectDeletion(in);
+}
+
+static int yaffs_DeleteHardLink(yaffs_Object *in)
+{
+ // remove this hardlink from the list assocaited with the equivalent
+ // object
+ list_del(&in->hardLinks);
+ return yaffs_DoGenericObjectDeletion(in);
+}
+
+
+static int yaffs_UnlinkWorker(yaffs_Object *obj)
+{
+
+
+ if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
+ {
+ return yaffs_DeleteHardLink(obj);
+ }
+ else if(!list_empty(&obj->hardLinks))
+ {
+#if 0
+ // Curve ball: We're unlinking an object that has a hardlink.
+ // Therefore we can't really delete the object.
+ // Instead, we do the following:
+ // - Select a hardlink.
+ // - Re-type a hardlink as the equivalent object and populate the fields, including the
+ // objectId. Updating the object id is important so that all the hardlinks do not need
+ // to be rewritten.
+ // - Update the equivalet object pointers.
+ // - Delete all object.
+
+ yaffs_Object *hl;
+ struct list_head *i;
+
+
+ yaffs_RemoveObjectFromDirectory(obj);
+
+
+
+ hl = list_entry(obj->hardLinks.next, yaffs_Object,hardLinks);
+
+ hl->dirty = 1;
+ hl->st_mode = obj->st_mode;
+ hl->st_uid = obj->st_uid;
+ hl->st_gid = obj->st_gid;
+ hl->st_atime = obj->st_atime;
+ hl->st_mtime = obj->st_mtime;
+ hl->st_ctime = obj->st_ctime;
+ hl->st_rdev = obj->st_rdev;
+
+ hl->variantType = obj->variantType;
+
+ switch(hl->variantType)
+ {
+ case YAFFS_OBJECT_TYPE_FILE:
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ // These types are OK to just copy across.
+ hl->variant = obj->variant;
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ // Fix the list up
+ list_add(&hl->variant.directoryVariant.children,
+ &obj->variant.directoryVariant.children);
+ list_del(&obj->variant.directoryVariant.children);
+
+ // Now change all the directory children to point to the new parent.
+ list_for_each(i,&hl->variant.directoryVariant.children)
+ {
+ list_entry(i,yaffs_Object,siblings)->parent = hl;
+ }
+ break;
+
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ // Should not be either of these types.
+ }
+
+ // Now fix up the hardlink chain
+ list_del(&obj->hardLinks);
+
+ list_for_each(i,&hl->hardLinks)
+ {
+ list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObject = hl;
+ list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObjectId = hl->objectId;
+ }
+
+ // Now fix up the hash links.
+ yaffs_UnhashObject(hl);
+ hl->objectId = obj->objectId;
+ yaffs_HashObject(hl);
+
+ // Update the hardlink which has become an object
+ yaffs_UpdateObjectHeader(hl,NULL);
+
+ // Finally throw away the deleted object
+ yaffs_DeleteChunk(obj->myDev,obj->chunkId);
+ yaffs_FreeObject(obj);
+
+ return YAFFS_OK;
+#else
+ // Curve ball: We're unlinking an object that has a hardlink.
+ //
+ // This problem arises because we are not strictly following
+ // The Linux link/inode model.
+ //
+ // We can't really delete the object.
+ // Instead, we do the following:
+ // - Select a hardlink.
+ // - Unhook it from the hard links
+ // - Unhook it from its parent directory (so that the rename can work)
+ // - Rename the object to the hardlink's name.
+ // - Delete the hardlink
+
+
+ yaffs_Object *hl;
+ int retVal;
+ char name[YAFFS_MAX_NAME_LENGTH+1];
+
+ hl = list_entry(obj->hardLinks.next,yaffs_Object,hardLinks);
+
+ list_del_init(&hl->hardLinks);
+ list_del_init(&hl->siblings);
+
+ yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);
+
+ retVal = yaffs_ChangeObjectName(obj, hl->parent, name);
+
+ if(retVal == YAFFS_OK)
+ {
+ retVal = yaffs_DoGenericObjectDeletion(hl);
+ }
+ return retVal;
+
+#endif
+
+
+ }
+ else
+ {
+ switch(obj->variantType)
+ {
+ case YAFFS_OBJECT_TYPE_FILE:
+ return yaffs_DeleteFile(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ return yaffs_DeleteDirectory(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ return yaffs_DeleteSymLink(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ default:
+ return YAFFS_FAIL;
+ }
+ }
+}
+
+int yaffs_Unlink(yaffs_Object *dir, const char *name)
+{
+ yaffs_Object *obj;
+
+ obj = yaffs_FindObjectByName(dir,name);
+
+ if(obj && obj->unlinkAllowed)
+ {
+ return yaffs_UnlinkWorker(obj);
+ }
+
+ return YAFFS_FAIL;
+
+}
+
+//////////////// Initialisation Scanning /////////////////
+
+
+
+// For now we use the SmartMedia check.
+// We look at the blockStatus byte in the first two chunks
+// These must be 0xFF to pass as OK.
+// todo: this function needs to be modifyable foir different NAND types
+// and different chunk sizes. Suggest make this into a per-device configurable
+// function.
+static int yaffs_IsBlockBad(yaffs_Device *dev, int blk)
+{
+ yaffs_Spare spare;
+
+ yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK,NULL,&spare);
+ if(spare.blockStatus != 0xFF)
+ {
+ return 1;
+ }
+ yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK + 1,NULL,&spare);
+ if(spare.blockStatus != 0xFF)
+ {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+static int yaffs_Scan(yaffs_Device *dev)
+{
+ yaffs_Spare spare;
+ yaffs_Tags tags;
+ int blk;
+ int chunk;
+ int c;
+ int deleted;
+ yaffs_BlockState state;
+ yaffs_Object *hardList = NULL;
+ yaffs_Object *hl;
+
+
+
+ yaffs_ObjectHeader *oh;
+ yaffs_Object *in;
+ yaffs_Object *parent;
+
+ __u8 chunkData[YAFFS_BYTES_PER_CHUNK];
+
+
+ // Scan all the blocks...
+
+ for(blk = dev->startBlock; blk <= dev->endBlock; blk++)
+ {
+ deleted = 0;
+ dev->blockInfo[blk].pageBits = 0;
+ dev->blockInfo[blk].pagesInUse = 0;
+ state = YAFFS_BLOCK_STATE_SCANNING;
+
+
+ if(yaffs_IsBlockBad(dev,blk))
+ {
+ state = YAFFS_BLOCK_STATE_DEAD;
+ T((TSTR("block %d is bad" TENDSTR),blk));
+ }
+
+ // Read each chunk in the block.
+
+ for(c = 0; c < YAFFS_CHUNKS_PER_BLOCK &&
+ state == YAFFS_BLOCK_STATE_SCANNING; c++)
+ {
+ // Read the spare area and decide what to do
+ chunk = blk * YAFFS_CHUNKS_PER_BLOCK + c;
+
+ yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare);
+
+
+ // This block looks ok, now what's in this chunk?
+ yaffs_GetTagsFromSpare(&spare,&tags);
+
+ if(yaffs_countBits[spare.pageStatus] < 6)
+ {
+ // A deleted chunk
+ deleted++;
+ dev->nFreeChunks ++;
+ //T((" %d %d deleted\n",blk,c));
+ }
+ else if(tags.objectId == YAFFS_UNUSED_OBJECT_ID)
+ {
+ // An unassigned chunk in the block
+ // This means that either the block is empty or
+ // this is the one being allocated from
+
+ if(c == 0)
+ {
+ // the block is unused
+ state = YAFFS_BLOCK_STATE_EMPTY;
+ dev->nErasedBlocks++;
+ }
+ else
+ {
+ // this is the block being allocated from
+ T((TSTR(" Allocating from %d %d" TENDSTR),blk,c));
+ state = YAFFS_BLOCK_STATE_ALLOCATING;
+ dev->allocationBlock = blk;
+ dev->allocationPage = c;
+ }
+
+ dev->nFreeChunks += (YAFFS_CHUNKS_PER_BLOCK - c);
+ }
+ else if(tags.chunkId > 0)
+ {
+ int endpos;
+ // A data chunk.
+ dev->blockInfo[blk].pageBits |= (1 << c);
+ dev->blockInfo[blk].pagesInUse++;
+
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
+ // PutChunkIntoFIle checks for a clash (two data chunks with
+ // the same chunkId).
+ yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);
+ endpos = (tags.chunkId - 1)* YAFFS_BYTES_PER_CHUNK + tags.byteCount;
+ if(in->variant.fileVariant.scannedFileSize variant.fileVariant.scannedFileSize = endpos;
+ }
+ //T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));
+ }
+ else
+ {
+ // chunkId == 0, so it is an ObjectHeader.
+ // Thus, we read in the object header and make the object
+ dev->blockInfo[blk].pageBits |= (1 << c);
+ dev->blockInfo[blk].pagesInUse++;
+
+ yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL);
+
+ oh = (yaffs_ObjectHeader *)chunkData;
+
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
+
+ if(in->valid)
+ {
+ // We have already filled this one. We have a duplicate and need to resolve it.
+
+ unsigned existingSerial = in->serial;
+ unsigned newSerial = tags.serialNumber;
+
+ if(((existingSerial+1) & 3) == newSerial)
+ {
+ // Use new one - destroy the exisiting one
+ yaffs_DeleteChunk(dev,in->chunkId);
+ in->valid = 0;
+ }
+ else
+ {
+ // Use existing - destroy this one.
+ yaffs_DeleteChunk(dev,chunk);
+ }
+ }
+
+ if(!in->valid)
+ {
+ // we need to load this info
+
+ in->valid = 1;
+ in->variantType = oh->type;
+
+ in->st_mode = oh->st_mode;
+ in->st_uid = oh->st_uid;
+ in->st_gid = oh->st_gid;
+ in->st_atime = oh->st_atime;
+ in->st_mtime = oh->st_mtime;
+ in->st_ctime = oh->st_ctime;
+ in->st_rdev = oh->st_rdev;
+ in->chunkId = chunk;
+
+ in->sum = oh->sum;
+ in->dirty = 0;
+
+ // directory stuff...
+ // hook up to parent
+
+ parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY);
+ if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN)
+ {
+ // Set up as a directory
+ parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY;
+ INIT_LIST_HEAD(&parent->variant.directoryVariant.children);
+ }
+ else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+ {
+ // Hoosterman, another problem....
+ // We're trying to use a non-directory as a directory
+ // Todo ... handle
+ }
+
+ yaffs_AddObjectToDirectory(parent,in);
+
+ // Note re hardlinks.
+ // Since we might scan a hardlink before its equivalent object is scanned
+ // we put them all in a list.
+ // After scanning is complete, we should have all the objects, so we run through this
+ // list and fix up all the chains.
+
+ switch(in->variantType)
+ {
+ case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem
+ break;
+ case YAFFS_OBJECT_TYPE_FILE:
+ in->variant.fileVariant.fileSize = oh->fileSize;
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
+ (yaffs_Object *)(in->hardLinks.next) = hardList;
+ hardList = in;
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY: // Do nothing
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL: // Do nothing
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK: // Do nothing
+ in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);
+ break;
+ }
+ //T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));
+ }
+ }
+ }
+
+ if(state == YAFFS_BLOCK_STATE_SCANNING)
+ {
+ // If we got this far while scanning, then the block is fully allocated.
+ state = YAFFS_BLOCK_STATE_FULL;
+ }
+
+ dev->blockInfo[blk].blockState = state;
+
+ // Now let's see if it was dirty
+ if( dev->blockInfo[blk].pagesInUse == 0 &&
+ dev->blockInfo[blk].blockState == YAFFS_BLOCK_STATE_FULL)
+ {
+ yaffs_BlockBecameDirty(dev,blk);
+ }
+
+ }
+
+ // Fix up the hard link chains.
+ // We should now have scanned all the objects, now it's time to add these
+ // hardlinks.
+ while(hardList)
+ {
+ hl = hardList;
+ hardList = (yaffs_Object *)(hardList->hardLinks.next);
+
+ in = yaffs_FindObjectByNumber(dev,hl->variant.hardLinkVariant.equivalentObjectId);
+
+ if(in)
+ {
+ // Add the hardlink pointers
+ hl->variant.hardLinkVariant.equivalentObject=in;
+ list_add(&hl->hardLinks,&in->hardLinks);
+ }
+ else
+ {
+ //Todo Need to report/handle this better.
+ // Got a problem... hardlink to a non-existant object
+ hl->variant.hardLinkVariant.equivalentObject=NULL;
+ INIT_LIST_HEAD(&hl->hardLinks);
+
+ }
+
+ }
+
+
+
+ return YAFFS_OK;
+}
+
+
+////////////////////////// Directory Functions /////////////////////////
+
+
+static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj)
+{
+
+ if(obj->siblings.prev == NULL)
+ {
+ // Not initialised
+ INIT_LIST_HEAD(&obj->siblings);
+
+ }
+ else if(!list_empty(&obj->siblings))
+ {
+ // If it is holed up somewhere else, un hook it
+ list_del_init(&obj->siblings);
+ }
+ // Now add it
+ list_add(&obj->siblings,&directory->variant.directoryVariant.children);
+ obj->parent = directory;
+}
+
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)
+{
+ list_del_init(&obj->siblings);
+ obj->parent = NULL;
+}
+
+yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const char *name)
+{
+ int sum;
+
+ struct list_head *i;
+ char buffer[YAFFS_MAX_NAME_LENGTH+1];
+
+ yaffs_Object *l;
+
+ sum = yaffs_CalcNameSum(name);
+
+ list_for_each(i,&directory->variant.directoryVariant.children)
+ {
+ l = list_entry(i, yaffs_Object,siblings);
+
+ // Special case for lost-n-found
+ if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)
+ {
+ if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)
+ {
+ return l;
+ }
+ }
+ else if(yaffs_SumCompare(l->sum, sum))
+ {
+ // Do a real check
+ yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH);
+ if(yaffs_strcmp(name,buffer) == 0)
+ {
+ return l;
+ }
+
+
+ }
+ }
+
+ return NULL;
+}
+
+
+int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *))
+{
+ struct list_head *i;
+ yaffs_Object *l;
+
+
+ list_for_each(i,&theDir->variant.directoryVariant.children)
+ {
+ l = list_entry(i, yaffs_Object,siblings);
+ if(!fn(l))
+ {
+ return YAFFS_FAIL;
+ }
+ }
+
+ return YAFFS_OK;
+
+}
+
+
+// GetEquivalentObject dereferences any hard links to get to the
+// actual object.
+
+yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj)
+{
+ if(obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
+ {
+ // We want the object id of the equivalent object, not this one
+ obj = obj->variant.hardLinkVariant.equivalentObject;
+ }
+ return obj;
+
+}
+
+int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize)
+{
+ memset(name,0,buffSize);
+
+ if(obj->objectId == YAFFS_OBJECTID_LOSTNFOUND)
+ {
+ strncpy(name,YAFFS_LOSTNFOUND_NAME,buffSize - 1);
+ }
+ else if(obj->chunkId <= 0)
+ {
+ char locName[20];
+ // make up a name
+ sprintf(locName,"%s%d",YAFFS_LOSTNFOUND_PREFIX,obj->objectId);
+ strncpy(name,locName,buffSize - 1);
+
+ }
+ else
+ {
+ __u8 buffer[YAFFS_BYTES_PER_CHUNK];
+ yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
+
+ memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
+
+ if(obj->chunkId >= 0)
+ {
+ yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);
+ }
+ strncpy(name,oh->name,buffSize - 1);
+ }
+
+ return strlen(name);
+}
+
+int yaffs_GetObjectFileLength(yaffs_Object *obj)
+{
+
+ // Dereference any hard linking
+ obj = yaffs_GetEquivalentObject(obj);
+
+ if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)
+ {
+ return obj->variant.fileVariant.fileSize;
+ }
+ if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
+ {
+ return strlen(obj->variant.symLinkVariant.alias);
+ }
+ else
+ {
+ // Only a directory should drop through to here
+ return YAFFS_BYTES_PER_CHUNK;
+ }
+}
+
+int yaffs_GetObjectLinkCount(yaffs_Object *obj)
+{
+ int count = 1; // the object itself
+ struct list_head *i;
+
+ list_for_each(i,&obj->hardLinks)
+ {
+ count++;
+ }
+ return count;
+
+}
+
+
+int yaffs_GetObjectInode(yaffs_Object *obj)
+{
+ obj = yaffs_GetEquivalentObject(obj);
+
+ return obj->objectId;
+}
+
+unsigned yaffs_GetObjectType(yaffs_Object *obj)
+{
+ obj = yaffs_GetEquivalentObject(obj);
+
+ switch(obj->variantType)
+ {
+ case YAFFS_OBJECT_TYPE_FILE: return DT_REG; break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY: return DT_DIR; break;
+ case YAFFS_OBJECT_TYPE_SYMLINK: return DT_LNK; break;
+ case YAFFS_OBJECT_TYPE_HARDLINK: return DT_REG; break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ if(S_ISFIFO(obj->st_mode)) return DT_FIFO;
+ if(S_ISCHR(obj->st_mode)) return DT_CHR;
+ if(S_ISBLK(obj->st_mode)) return DT_BLK;
+ if(S_ISSOCK(obj->st_mode)) return DT_SOCK;
+ default: return DT_REG; break;
+ }
+}
+
+char *yaffs_GetSymlinkAlias(yaffs_Object *obj)
+{
+ obj = yaffs_GetEquivalentObject(obj);
+ if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
+ {
+ return yaffs_CloneString(obj->variant.symLinkVariant.alias);
+ }
+ else
+ {
+ return yaffs_CloneString("");
+ }
+}
+
+
+int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)
+{
+ unsigned int valid = attr->ia_valid;
+
+ if(valid & ATTR_MODE) obj->st_mode = attr->ia_mode;
+ if(valid & ATTR_UID) obj->st_uid = attr->ia_uid;
+ if(valid & ATTR_GID) obj->st_gid = attr->ia_gid;
+
+ if(valid & ATTR_ATIME) obj->st_atime = attr->ia_atime;
+ if(valid & ATTR_CTIME) obj->st_ctime = attr->ia_ctime;
+ if(valid & ATTR_MTIME) obj->st_mtime = attr->ia_mtime;
+
+ if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);
+
+ yaffs_UpdateObjectHeader(obj,NULL);
+
+ return YAFFS_OK;
+
+}
+int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)
+{
+ unsigned int valid = 0;
+
+ attr->ia_mode = obj->st_mode; valid |= ATTR_MODE;
+ attr->ia_uid = obj->st_uid; valid |= ATTR_UID;
+ attr->ia_gid = obj->st_gid; valid |= ATTR_GID;
+
+ attr->ia_atime = obj->st_atime; valid |= ATTR_ATIME;
+ attr->ia_ctime = obj->st_ctime; valid |= ATTR_CTIME;
+ attr->ia_mtime = obj->st_mtime; valid |= ATTR_MTIME;
+
+ attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;
+
+ attr->ia_valid = valid;
+
+ return YAFFS_OK;
+
+}
+
+
+
+int yaffs_DumpObject(yaffs_Object *obj)
+{
+// __u8 buffer[YAFFS_BYTES_PER_CHUNK];
+ char name[257];
+// yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
+
+// memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
+
+// if(obj->chunkId >= 0)
+// {
+// yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);
+// }
+
+ yaffs_GetObjectName(obj,name,256);
+
+ YPRINTF(("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n",
+ obj->objectId,yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial,
+ obj->sum, obj->chunkId, yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));
+
+#if 0
+ YPRINTF(("Object %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d\n",
+ obj->objectId, oh->name, obj->dirty, obj->valid, obj->serial,
+ obj->sum, obj->chunkId));
+ switch(obj->variantType)
+ {
+ case YAFFS_OBJECT_TYPE_FILE:
+ YPRINTF((" FILE length %d\n",obj->variant.fileVariant.fileSize));
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ YPRINTF((" DIRECTORY\n"));
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK: //todo
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ default:
+ }
+#endif
+
+ return YAFFS_OK;
+}
+
+
+///////////////////////// Initialisation code ///////////////////////////
+
+
+
+int yaffs_GutsInitialise(yaffs_Device *dev)
+{
+ unsigned nChunks,x;
+ int bits;
+
+
+ dev = dev;
+
+ if(!yaffs_CheckStructures())
+ {
+ return YAFFS_FAIL;
+ }
+
+
+ // OK now calculate a few things for the device
+ // Calculate chunkGroupBits.
+ // If there are 64k or less chunks then this is 1
+ // Else it is log2(nChunks) - 16
+ //
+ x = nChunks = YAFFS_CHUNKS_PER_BLOCK * dev->nBlocks;
+
+ for(bits = 0, x = nChunks; (x & 1) == 0; bits++)
+ {
+ x >>= 1;
+ }
+
+ if( x != 1)
+ {
+ // Not a power of 2
+ YPRINTF(("nBlocks should be a power of 2 but is %u\n",
+ dev->nBlocks));
+ return YAFFS_FAIL;
+ }
+
+ if(bits <= 16)
+ {
+ dev->chunkGroupBits = 0;
+ dev->chunkGroupSize = 1;
+ }
+ else
+ {
+ dev->chunkGroupBits = bits - 16;
+ dev->chunkGroupSize = nChunks/0x10000;
+ }
+
+ // More device initialisation
+ dev->garbageCollectionRequired = 0;
+ dev->currentDirtyChecker = 0;
+
+ yaffs_InitialiseBlocks(dev);
+
+ yaffs_InitialiseTnodes(dev);
+
+ yaffs_InitialiseObjects(dev);
+
+
+ // Initialise the root and lost and found directories
+ dev->lostNFoundDir = dev->rootDir = NULL;
+ dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR);
+ dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_ROOT_MODE | S_IFDIR);
+ yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir);
+
+ // Now scan the flash.
+ yaffs_Scan(dev);
+
+ // Zero out stats
+ dev->nPageReads = 0;
+ dev->nPageWrites = 0;
+ dev->nBlockErasures = 0;
+ dev->nGCCopies = 0;
+ dev->nRetriedWrites = 0;
+
+
+ return YAFFS_OK;
+
+}
+
+void yaffs_Deinitialise(yaffs_Device *dev)
+{
+ yaffs_DeinitialiseBlocks(dev);
+ yaffs_DeinitialiseTnodes(dev);
+ yaffs_DeinitialiseObjects(dev);
+
+}
+
+int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
+{
+ int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS);
+
+ return (nFree < 0) ? 0 : nFree;
+
+}
+
+
+/////////////////// YAFFS test code //////////////////////////////////
+
+#define yaffs_CheckStruct(structure,syze, name) \
+ if(sizeof(structure) != syze) \
+ { YPRINTF(("%s should be %d but is %d\n",name,syze,sizeof(structure))); \
+ return YAFFS_FAIL; \
+ }
+
+
+static int yaffs_CheckStructures(void)
+{
+ yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags")
+ yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion")
+ yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare")
+ yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode")
+ yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader")
+
+
+ return YAFFS_OK;
+}
+
+void yaffs_GutsTest(yaffs_Device *dev)
+{
+
+ if(yaffs_CheckStructures() != YAFFS_OK)
+ {
+ YPRINTF(("One or more structures malformed-- aborting\n"));
+ return;
+ }
+ else
+ {
+ YPRINTF(("Structures OK\n"));
+ }
+
+ yaffs_TnodeTest(dev);
+ yaffs_ObjectTest(dev);
+}
+
+
diff --git a/yaffs_guts.h b/yaffs_guts.h
index 3ae36e7..d37ac44 100644
--- a/yaffs_guts.h
+++ b/yaffs_guts.h
@@ -1,444 +1,466 @@
-/*
- * YAFFS: Yet another FFS. A NAND-flash specific file system.
- * yaffs_guts.h: Configuration etc for yaffs_guts
- *
- * Copyright (C) 2002 Aleph One Ltd.
- * for Toby Churchill Ltd and Brightstar Engineering
- *
- * Created by Charles Manning
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#ifndef __YAFFS_GUTS_H__
-#define __YAFFS_GUTS_H__
-
-#include "devextras.h"
-
-
-#define YAFFS_OK 1
-#define YAFFS_FAIL 0
-
-// Y=0x59, A=0x41, S=0x53
-#define YAFFS_MAGIC 0x5941FF53
-
-#define YAFFS_NTNODES_LEVEL0 16
-#define YAFFS_TNODES_LEVEL0_BITS 4
-#define YAFFS_TNODES_LEVEL0_MASK 0xf
-
-#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2)
-#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1)
-#define YAFFS_TNODES_INTERNAL_MASK 0x7
-#define YAFFS_TNODES_MAX_LEVEL 6
-
-#define YAFFS_BYTES_PER_CHUNK 512
-#define YAFFS_CHUNK_SIZE_SHIFT 9
-
-#define YAFFS_BYTES_PER_SPARE 16
-
-#define YAFFS_CHUNKS_PER_BLOCK 32
-#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
-
-#define YAFFS_MAX_CHUNK_ID 0x000FFFFF
-
-#define YAFFS_UNUSED_OBJECT_ID 0x0003FFFF
-
-#define YAFFS_ALLOCATION_NOBJECTS 100
-#define YAFFS_ALLOCATION_NTNODES 100
-#define YAFFS_ALLOCATION_NLINKS 100
-
-#define YAFFS_NOBJECT_BUCKETS 256
-
-
-#define YAFFS_RESERVED_BLOCKS 5
-
-#define YAFFS_OBJECT_SPACE 0x40000
-#define YAFFS_MAX_NAME_LENGTH 255
-
-#define YAFFS_MAX_ALIAS_LENGTH 211
-
-#define YAFFS_OBJECTID_ROOT 1
-#define YAFFS_ROOT_MODE 0666
-#define YAFFS_OBJECTID_LOSTNFOUND 2
-#define YAFFS_LOSTNFOUND_NAME "lost+found"
-
-// Tags structures in RAM
-// NB This uses bitfield. Bitfields should not stradle a u32 boundary otherwise
-// the structure size will get blown out.
-
-typedef struct
-{
- unsigned chunkId:20;
- unsigned serialNumber:2;
- unsigned byteCount:10;
- unsigned objectId:18;
- unsigned ecc:12;
- unsigned unusedStuff:2;
-} yaffs_Tags;
-
-typedef union
-{
- yaffs_Tags asTags;
- __u8 asBytes[8];
-} yaffs_TagsUnion;
-
-
-// Spare structure
-typedef struct
-{
- __u8 tagByte0;
- __u8 tagByte1;
- __u8 tagByte2;
- __u8 tagByte3;
- __u8 pageStatus; // Currently unused, but sort of set aside to distinguish
- // unused - vs- used -vs- deleted chunks. We achieve this by
- // using the objectId tags.
- __u8 blockStatus;
- __u8 tagByte4;
- __u8 tagByte5;
- __u8 ecc1[3];
- __u8 tagByte6;
- __u8 tagByte7;
- __u8 ecc2[3];
-} yaffs_Spare;
-
-// Block data in RAM
-
-typedef enum {
- YAFFS_BLOCK_STATE_UddNKNOWN = 0,
- YAFFS_BLOCK_STATE_SCANNING, // Used while the block is being scanned.
- // NB Don't erase blocks while they're being scanned
-
- YAFFS_BLOCK_STATE_EMPTY, // This block is empty
-
- YAFFS_BLOCK_STATE_ALLOCATING, // This block is partially allocated.
- // This is the one currently being used for page
- // allocation. Should never be more than one of these
-
-
- YAFFS_BLOCK_STATE_FULL, // All the pages in this block have been allocated.
- // At least one page holds valid data.
-
- YAFFS_BLOCK_STATE_DIRTY, // All pages have been allocated and deleted.
- // Erase me, reuse me.
-
- YAFFS_BLOCK_STATE_DEAD = 0x99 // This block has failed and is not in use
-
-} yaffs_BlockState;
-
-
-
-
-typedef struct
-{
- __u32 pageBits; // bitmap of pages in use
- __u8 blockState; // One of the above block states
- __u8 pagesInUse; // number of pages in use
-} yaffs_BlockInfo;
-
-
-//////////////////// Object structure ///////////////////////////
-// This is the object structure as stored on NAND
-
-typedef enum
-{
- YAFFS_OBJECT_TYPE_UNKNOWN,
- YAFFS_OBJECT_TYPE_FILE,
- YAFFS_OBJECT_TYPE_SYMLINK,
- YAFFS_OBJECT_TYPE_DIRECTORY,
- YAFFS_OBJECT_TYPE_HARDLINK
-} yaffs_ObjectType;
-
-typedef struct
-{
- yaffs_ObjectType type;
-
- // Apply to everything
- int parentObjectId;
- __u16 sum; // checksum of name
- char name[YAFFS_MAX_NAME_LENGTH + 1];
-
- // Thes following apply to directories, files, symlinks - not hard links
- __u32 st_mode; // protection
- __u32 st_uid; // user ID of owner
- __u32 st_gid; // group ID of owner
- __u32 st_atime; // time of last access
- __u32 st_mtime; // time of last modification
- __u32 st_ctime; // time of last change
-
- // File size applies to files only
- int fileSize;
-
- // Equivalent object id applies to hard links only.
- int equivalentObjectId;
- char alias[YAFFS_MAX_ALIAS_LENGTH + 1];
-
-
-} yaffs_ObjectHeader;
-
-
-
-//////////////////// Tnode ///////////////////////////
-
-union yaffs_Tnode_union
-{
- union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL];
- __u16 level0[YAFFS_NTNODES_LEVEL0];
-
-};
-
-typedef union yaffs_Tnode_union yaffs_Tnode;
-
-struct yaffs_TnodeList_struct
-{
- struct yaffs_TnodeList_struct *next;
- yaffs_Tnode *tnodes;
-};
-
-typedef struct yaffs_TnodeList_struct yaffs_TnodeList;
-
-
-
-/////////////////// Object ////////////////////////////////
-// An object can be one of:
-// - a directory (no data, has children links
-// - a regular file (data.... not prunes :->).
-// - a symlink [symbolic link] (the alias).
-// - a hard link
-
-
-typedef struct
-{
- __u32 fileSize;
- __u32 topLevel;
- yaffs_Tnode *top;
-} yaffs_FileStructure;
-
-typedef struct
-{
- struct list_head children; // list of child links
-} yaffs_DirectoryStructure;
-
-typedef struct
-{
- char *alias;
-} yaffs_SymLinkStructure;
-
-typedef struct
-{
- struct yaffs_ObjectStruct *equivalentObject;
- __u32 equivalentObjectId;
-} yaffs_HardLinkStructure;
-
-typedef union
-{
- yaffs_FileStructure fileVariant;
- yaffs_DirectoryStructure directoryVariant;
- yaffs_SymLinkStructure symLinkVariant;
- yaffs_HardLinkStructure hardLinkVariant;
-} yaffs_ObjectVariant;
-
-
-struct yaffs_ObjectStruct
-{
- __u8 fake:1; // A fake object has no presence on NAND.
- __u8 renameAllowed:1;
- __u8 unlinkAllowed:1;
- __u8 dirty:1; // the object needs to be written to flash
- __u8 valid:1; // When the file system is being loaded up, this
- // object might be created before the data
- // is available (ie. file data records appear before the header).
- __u8 serial; // serial number of chunk in NAND. Store here so we don't have to
- // read back the old one to update.
- __u16 sum; // sum of the name to speed searching
-
- struct yaffs_DeviceStruct *myDev; // The device I'm on
-
-
- struct list_head hashLink; // list of objects in this hash bucket
-
-
- struct list_head hardLinks; // all the equivalent hard linked objects
- // live on this list
- // directory structure stuff
- struct yaffs_ObjectStruct *parent; //my parent directory
- struct list_head siblings; // siblings in a directory
- // also used for linking up the free list
-
- // Where's my data in NAND?
- int chunkId; // where it lives
-
- __u32 objectId; // the object id value
-
-
- __u32 st_mode; // protection
- __u32 st_uid; // user ID of owner
- __u32 st_gid; // group ID of owner
- __u32 st_atime; // time of last access
- __u32 st_mtime; // time of last modification
- __u32 st_ctime; // time of last change
-
-
- yaffs_ObjectType variantType;
-
- yaffs_ObjectVariant variant;
-
-};
-
-
-
-typedef struct yaffs_ObjectStruct yaffs_Object;
-
-
-struct yaffs_ObjectList_struct
-{
- yaffs_Object *objects;
- struct yaffs_ObjectList_struct *next;
-};
-
-typedef struct yaffs_ObjectList_struct yaffs_ObjectList;
-
-typedef struct
-{
- struct list_head list;
- __u32 count;
-} yaffs_ObjectBucket;
-
-
-//////////////////// Device ////////////////////////////////
-
-struct yaffs_DeviceStruct
-{
- // Entry parameters set up way early. Yaffs sets up the rest.
- __u32 nBlocks; // Size of whole device in blocks
- __u32 startBlock; // Start block we're allowed to use
- __u32 endBlock; // End block we're allowed to use
- __u16 chunkGroupBits; // 0 for devices <= 32MB. else log2(nchunks) - 16
- __u16 chunkGroupSize; // == 2^^chunkGroupBits
-
-
- void *genericDevice; // Pointer to device context
- // On an mtd this holds the mtd pointer.
-
-#ifdef __KERNEL__
-
- struct semaphore sem;// Semaphore for waiting on erasure.
-
-#endif
-
-
- // NAND access functions (Must be set before calling YAFFS)
-
- int (*writeChunkToNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare);
- int (*readChunkFromNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
- int (*eraseBlockInNAND)(struct yaffs_DeviceStruct *dev,int blockInNAND);
- int (*initialiseNAND)(struct yaffs_DeviceStruct *dev);
-
-#ifdef __KERNEL__
- void (*putSuperFunc)(struct super_block *sb);
-#endif
-
- // Runtime parameters.
- yaffs_BlockInfo *blockInfo;
- int nErasedBlocks;
- int allocationBlock;
- __u32 allocationPage;
-
- // Runtime state
- int nTnodesCreated;
- yaffs_Tnode *freeTnodes;
- int nFreeTnodes;
- yaffs_TnodeList *allocatedTnodeList;
-
-
- int nObjectsCreated;
- yaffs_Object *freeObjects;
- int nFreeObjects;
-
- yaffs_ObjectList *allocatedObjectList;
-
- yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS];
-
- int nFreeChunks;
-
- int currentDirtyChecker; // Used to find current dirtiest block
-
- int garbageCollectionRequired;
-
- yaffs_Object *rootDir;
- yaffs_Object *lostNFoundDir;
-
-
-};
-
- typedef struct yaffs_DeviceStruct yaffs_Device;
-
-
-
-//////////// YAFFS Functions //////////////////
-
-int yaffs_GutsInitialise(yaffs_Device *dev);
-void yaffs_Deinitialise(yaffs_Device *dev);
-
-int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev);
-
-
-// Rename
-int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName);
-
-// generic Object functions
-int yaffs_Unlink(yaffs_Object *dir, const char *name);
-
-// Object access functions.
-int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize);
-int yaffs_GetObjectFileLength(yaffs_Object *obj);
-int yaffs_GetObjectInode(yaffs_Object *obj);
-unsigned yaffs_GetObjectType(yaffs_Object *obj);
-int yaffs_GetObjectLinkCount(yaffs_Object *obj);
-
-// Change inode attributes
-int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr);
-int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr);
-
-// File operations
-int yaffs_ReadDataFromFile(yaffs_Object *obj, __u8 *buffer, __u32 offset, int nBytes);
-int yaffs_WriteDataToFile(yaffs_Object *obj, const __u8 *buffer, __u32 offset, int nBytes);
-int yaffs_ResizeFile(yaffs_Object *obj, int newSize);
-
-yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid);
-int yaffs_FlushFile(yaffs_Object *obj);
-
-
-// Directory operations
-yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid);
-yaffs_Object *yaffs_FindObjectByName(yaffs_Object *theDir,const char *name);
-int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *));
-
-yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number);
-
-// Link operations
-yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject);
-
-yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj);
-
-// Symlink operations
-yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent, const char *name, __u32 mode, __u32 uid, __u32 gid, const char *alias);
-char *yaffs_GetSymlinkAlias(yaffs_Object *obj);
-
-
-// Special directories
-yaffs_Object *yaffs_Root(yaffs_Device *dev);
-yaffs_Object *yaffs_LostNFound(yaffs_Device *dev);
-
-
-// Debug dump
-int yaffs_DumpObject(yaffs_Object *obj);
-
-
-void yaffs_GutsTest(yaffs_Device *dev);
-
-
-#endif
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ * yaffs_guts.h: Configuration etc for yaffs_guts
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ * for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __YAFFS_GUTS_H__
+#define __YAFFS_GUTS_H__
+
+#include "devextras.h"
+
+
+#define YAFFS_OK 1
+#define YAFFS_FAIL 0
+
+// Y=0x59, A=0x41, S=0x53
+#define YAFFS_MAGIC 0x5941FF53
+
+#define YAFFS_NTNODES_LEVEL0 16
+#define YAFFS_TNODES_LEVEL0_BITS 4
+#define YAFFS_TNODES_LEVEL0_MASK 0xf
+
+#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2)
+#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1)
+#define YAFFS_TNODES_INTERNAL_MASK 0x7
+#define YAFFS_TNODES_MAX_LEVEL 6
+
+#define YAFFS_BYTES_PER_CHUNK 512
+#define YAFFS_CHUNK_SIZE_SHIFT 9
+
+#define YAFFS_BYTES_PER_SPARE 16
+
+#define YAFFS_CHUNKS_PER_BLOCK 32
+#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
+
+#define YAFFS_MAX_CHUNK_ID 0x000FFFFF
+
+#define YAFFS_UNUSED_OBJECT_ID 0x0003FFFF
+
+#define YAFFS_ALLOCATION_NOBJECTS 100
+#define YAFFS_ALLOCATION_NTNODES 100
+#define YAFFS_ALLOCATION_NLINKS 100
+
+#define YAFFS_NOBJECT_BUCKETS 256
+
+
+#define YAFFS_RESERVED_BLOCKS 5
+
+#define YAFFS_OBJECT_SPACE 0x40000
+#define YAFFS_MAX_NAME_LENGTH 255
+
+#define YAFFS_MAX_ALIAS_LENGTH 159
+
+#define YAFFS_OBJECTID_ROOT 1
+#define YAFFS_OBJECTID_LOSTNFOUND 2
+
+// Tags structures in RAM
+// NB This uses bitfield. Bitfields should not stradle a u32 boundary otherwise
+// the structure size will get blown out.
+
+typedef struct
+{
+ unsigned chunkId:20;
+ unsigned serialNumber:2;
+ unsigned byteCount:10;
+ unsigned objectId:18;
+ unsigned ecc:12;
+ unsigned unusedStuff:2;
+} yaffs_Tags;
+
+typedef union
+{
+ yaffs_Tags asTags;
+ __u8 asBytes[8];
+} yaffs_TagsUnion;
+
+
+// Spare structure
+typedef struct
+{
+ __u8 tagByte0;
+ __u8 tagByte1;
+ __u8 tagByte2;
+ __u8 tagByte3;
+ __u8 pageStatus; // Currently unused, but sort of set aside to distinguish
+ // unused - vs- used -vs- deleted chunks. We achieve this by
+ // using the objectId tags.
+ __u8 blockStatus;
+ __u8 tagByte4;
+ __u8 tagByte5;
+ __u8 ecc1[3];
+ __u8 tagByte6;
+ __u8 tagByte7;
+ __u8 ecc2[3];
+} yaffs_Spare;
+
+// Block data in RAM
+
+typedef enum {
+ YAFFS_BLOCK_STATE_UddNKNOWN = 0,
+ YAFFS_BLOCK_STATE_SCANNING, // Used while the block is being scanned.
+ // NB Don't erase blocks while they're being scanned
+
+ YAFFS_BLOCK_STATE_EMPTY, // This block is empty
+
+ YAFFS_BLOCK_STATE_ALLOCATING, // This block is partially allocated.
+ // This is the one currently being used for page
+ // allocation. Should never be more than one of these
+
+
+ YAFFS_BLOCK_STATE_FULL, // All the pages in this block have been allocated.
+ // At least one page holds valid data.
+
+ YAFFS_BLOCK_STATE_DIRTY, // All pages have been allocated and deleted.
+ // Erase me, reuse me.
+
+ YAFFS_BLOCK_STATE_DEAD = 0x99 // This block has failed and is not in use
+
+} yaffs_BlockState;
+
+
+
+
+typedef struct
+{
+ __u32 pageBits; // bitmap of pages in use
+ __u8 blockState; // One of the above block states
+ __u8 pagesInUse; // number of pages in use
+} yaffs_BlockInfo;
+
+
+//////////////////// Object structure ///////////////////////////
+// This is the object structure as stored on NAND
+
+typedef enum
+{
+ YAFFS_OBJECT_TYPE_UNKNOWN,
+ YAFFS_OBJECT_TYPE_FILE,
+ YAFFS_OBJECT_TYPE_SYMLINK,
+ YAFFS_OBJECT_TYPE_DIRECTORY,
+ YAFFS_OBJECT_TYPE_HARDLINK,
+ YAFFS_OBJECT_TYPE_SPECIAL
+} yaffs_ObjectType;
+
+typedef struct
+{
+ yaffs_ObjectType type;
+
+ // Apply to everything
+ int parentObjectId;
+ __u16 sum; // checksum of name
+ char name[YAFFS_MAX_NAME_LENGTH + 1];
+
+ // Thes following apply to directories, files, symlinks - not hard links
+ __u32 st_mode; // protection
+ __u32 st_uid; // user ID of owner
+ __u32 st_gid; // group ID of owner
+ __u32 st_atime; // time of last access
+ __u32 st_mtime; // time of last modification
+ __u32 st_ctime; // time of last change
+
+ // File size applies to files only
+ int fileSize;
+
+ // Equivalent object id applies to hard links only.
+ int equivalentObjectId;
+
+ // Alias is for symlinks only.
+ char alias[YAFFS_MAX_ALIAS_LENGTH + 1];
+
+ __u32 st_rdev; // device stuff for block and char devices (maj/min)
+ // Roowm to grow...
+ __u32 roomToGrow[12];
+
+} yaffs_ObjectHeader;
+
+
+
+//////////////////// Tnode ///////////////////////////
+
+union yaffs_Tnode_union
+{
+ union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL];
+ __u16 level0[YAFFS_NTNODES_LEVEL0];
+
+};
+
+typedef union yaffs_Tnode_union yaffs_Tnode;
+
+struct yaffs_TnodeList_struct
+{
+ struct yaffs_TnodeList_struct *next;
+ yaffs_Tnode *tnodes;
+};
+
+typedef struct yaffs_TnodeList_struct yaffs_TnodeList;
+
+
+
+/////////////////// Object ////////////////////////////////
+// An object can be one of:
+// - a directory (no data, has children links
+// - a regular file (data.... not prunes :->).
+// - a symlink [symbolic link] (the alias).
+// - a hard link
+
+
+typedef struct
+{
+ __u32 fileSize;
+ __u32 scannedFileSize;
+ __u32 topLevel;
+ yaffs_Tnode *top;
+} yaffs_FileStructure;
+
+typedef struct
+{
+ struct list_head children; // list of child links
+} yaffs_DirectoryStructure;
+
+typedef struct
+{
+ char *alias;
+} yaffs_SymLinkStructure;
+
+typedef struct
+{
+ struct yaffs_ObjectStruct *equivalentObject;
+ __u32 equivalentObjectId;
+} yaffs_HardLinkStructure;
+
+typedef union
+{
+ yaffs_FileStructure fileVariant;
+ yaffs_DirectoryStructure directoryVariant;
+ yaffs_SymLinkStructure symLinkVariant;
+ yaffs_HardLinkStructure hardLinkVariant;
+} yaffs_ObjectVariant;
+
+
+struct yaffs_ObjectStruct
+{
+ __u8 fake:1; // A fake object has no presence on NAND.
+ __u8 renameAllowed:1;
+ __u8 unlinkAllowed:1;
+ __u8 dirty:1; // the object needs to be written to flash
+ __u8 valid:1; // When the file system is being loaded up, this
+ // object might be created before the data
+ // is available (ie. file data records appear before the header).
+ __u8 serial; // serial number of chunk in NAND. Store here so we don't have to
+ // read back the old one to update.
+ __u16 sum; // sum of the name to speed searching
+
+ struct yaffs_DeviceStruct *myDev; // The device I'm on
+
+
+ struct list_head hashLink; // list of objects in this hash bucket
+
+
+ struct list_head hardLinks; // all the equivalent hard linked objects
+ // live on this list
+ // directory structure stuff
+ struct yaffs_ObjectStruct *parent; //my parent directory
+ struct list_head siblings; // siblings in a directory
+ // also used for linking up the free list
+
+ // Where's my data in NAND?
+ int chunkId; // where it lives
+
+ int nDataChunks; // really only for debugging.
+
+ __u32 objectId; // the object id value
+
+
+ __u32 st_mode; // protection
+ __u32 st_uid; // user ID of owner
+ __u32 st_gid; // group ID of owner
+ __u32 st_atime; // time of last access
+ __u32 st_mtime; // time of last modification
+ __u32 st_ctime; // time of last change
+ __u32 st_rdev; // device stuff for block and char devices
+
+#ifdef WIN32
+ __u32 inUse;
+#endif
+
+
+ yaffs_ObjectType variantType;
+
+ yaffs_ObjectVariant variant;
+
+};
+
+
+
+typedef struct yaffs_ObjectStruct yaffs_Object;
+
+
+struct yaffs_ObjectList_struct
+{
+ yaffs_Object *objects;
+ struct yaffs_ObjectList_struct *next;
+};
+
+typedef struct yaffs_ObjectList_struct yaffs_ObjectList;
+
+typedef struct
+{
+ struct list_head list;
+ __u32 count;
+} yaffs_ObjectBucket;
+
+
+//////////////////// Device ////////////////////////////////
+
+struct yaffs_DeviceStruct
+{
+ // Entry parameters set up way early. Yaffs sets up the rest.
+ __u32 nBlocks; // Size of whole device in blocks
+ __u32 startBlock; // Start block we're allowed to use
+ __u32 endBlock; // End block we're allowed to use
+ __u16 chunkGroupBits; // 0 for devices <= 32MB. else log2(nchunks) - 16
+ __u16 chunkGroupSize; // == 2^^chunkGroupBits
+
+
+ void *genericDevice; // Pointer to device context
+ // On an mtd this holds the mtd pointer.
+
+#ifdef __KERNEL__
+
+ struct semaphore sem;// Semaphore for waiting on erasure.
+
+#endif
+
+
+ // NAND access functions (Must be set before calling YAFFS)
+
+ int (*writeChunkToNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare);
+ int (*readChunkFromNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
+ int (*eraseBlockInNAND)(struct yaffs_DeviceStruct *dev,int blockInNAND);
+ int (*initialiseNAND)(struct yaffs_DeviceStruct *dev);
+
+#ifdef __KERNEL__
+ void (*putSuperFunc)(struct super_block *sb);
+#endif
+
+ // Runtime parameters.
+ yaffs_BlockInfo *blockInfo;
+ int nErasedBlocks;
+ int allocationBlock;
+ __u32 allocationPage;
+
+ // Runtime state
+ int nTnodesCreated;
+ yaffs_Tnode *freeTnodes;
+ int nFreeTnodes;
+ yaffs_TnodeList *allocatedTnodeList;
+
+
+ int nObjectsCreated;
+ yaffs_Object *freeObjects;
+ int nFreeObjects;
+
+ yaffs_ObjectList *allocatedObjectList;
+
+ yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS];
+
+ int nFreeChunks;
+
+ int currentDirtyChecker; // Used to find current dirtiest block
+
+ int garbageCollectionRequired;
+
+ // Operations since mount
+ int nPageWrites;
+ int nPageReads;
+ int nBlockErasures;
+ int nGCCopies;
+ int nRetriedWrites;
+
+ yaffs_Object *rootDir;
+ yaffs_Object *lostNFoundDir;
+
+
+};
+
+ typedef struct yaffs_DeviceStruct yaffs_Device;
+
+
+
+//////////// YAFFS Functions //////////////////
+
+int yaffs_GutsInitialise(yaffs_Device *dev);
+void yaffs_Deinitialise(yaffs_Device *dev);
+
+int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev);
+
+
+// Rename
+int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName);
+
+// generic Object functions
+int yaffs_Unlink(yaffs_Object *dir, const char *name);
+
+// Object access functions.
+int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize);
+int yaffs_GetObjectFileLength(yaffs_Object *obj);
+int yaffs_GetObjectInode(yaffs_Object *obj);
+unsigned yaffs_GetObjectType(yaffs_Object *obj);
+int yaffs_GetObjectLinkCount(yaffs_Object *obj);
+
+// Change inode attributes
+int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr);
+int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr);
+
+// File operations
+int yaffs_ReadDataFromFile(yaffs_Object *obj, __u8 *buffer, __u32 offset, int nBytes);
+int yaffs_WriteDataToFile(yaffs_Object *obj, const __u8 *buffer, __u32 offset, int nBytes);
+int yaffs_ResizeFile(yaffs_Object *obj, int newSize);
+
+yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid);
+int yaffs_FlushFile(yaffs_Object *obj);
+
+
+// Directory operations
+yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid);
+yaffs_Object *yaffs_FindObjectByName(yaffs_Object *theDir,const char *name);
+int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *));
+
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number);
+
+// Link operations
+yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject);
+
+yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj);
+
+// Symlink operations
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent, const char *name, __u32 mode, __u32 uid, __u32 gid, const char *alias);
+char *yaffs_GetSymlinkAlias(yaffs_Object *obj);
+
+// Special inodes (fifos, sockets and devices)
+yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,__u32 rdev);
+
+
+// Special directories
+yaffs_Object *yaffs_Root(yaffs_Device *dev);
+yaffs_Object *yaffs_LostNFound(yaffs_Device *dev);
+
+
+// Debug dump
+int yaffs_DumpObject(yaffs_Object *obj);
+
+
+void yaffs_GutsTest(yaffs_Device *dev);
+
+
+#endif
diff --git a/yaffsdev b/yaffsdev
index 14777893ae8c047ed075a43b3ff7bbfad91a2087..4e083b0f7cfa448d6a10d70841ec5a5db6b81e12 100755
GIT binary patch
literal 109461
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 |