mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-01 06:25:55 +02:00
99f2f68b6f
Read the kernel command line from an otherwise unused area of the kernel image (will be used for changing the command line on the fly when copying the system from CF to NAND) git-svn-id: svn://svn.openwrt.org/openwrt/trunk@5296 3c298f89-4303-0410-b956-a3cf2f4a3e73
11287 lines
280 KiB
Diff
11287 lines
280 KiB
Diff
diff --git a/fs/Kconfig b/fs/Kconfig
|
|
index f9b5842..556f12a 100644
|
|
--- a/fs/Kconfig
|
|
+++ b/fs/Kconfig
|
|
@@ -1022,6 +1022,15 @@ config EFS_FS
|
|
To compile the EFS file system support as a module, choose M here: the
|
|
module will be called efs.
|
|
|
|
+config YAFFS_FS
|
|
+ tristate "Yet Another Flash File System (YAFFS) support"
|
|
+ depends on MTD
|
|
+ help
|
|
+ JFFS is the Journaling Flash File System developed by Axis
|
|
+ Communications in Sweden, aimed at providing a crash/powerdown-safe
|
|
+ file system for disk-less embedded devices. Further information is
|
|
+ available at (<http://developer.axis.com/software/jffs/>).
|
|
+
|
|
config JFFS_FS
|
|
tristate "Journalling Flash File System (JFFS) support"
|
|
depends on MTD
|
|
diff --git a/fs/Makefile b/fs/Makefile
|
|
index 078d3d1..2062d2f 100644
|
|
--- a/fs/Makefile
|
|
+++ b/fs/Makefile
|
|
@@ -84,6 +85,7 @@ obj-$(CONFIG_UFS_FS) += ufs/
|
|
obj-$(CONFIG_EFS_FS) += efs/
|
|
obj-$(CONFIG_JFFS_FS) += jffs/
|
|
obj-$(CONFIG_JFFS2_FS) += jffs2/
|
|
+obj-$(CONFIG_YAFFS_FS) += yaffs/
|
|
obj-$(CONFIG_AFFS_FS) += affs/
|
|
obj-$(CONFIG_ROMFS_FS) += romfs/
|
|
obj-$(CONFIG_QNX4FS_FS) += qnx4/
|
|
diff --git a/fs/yaffs/Makefile b/fs/yaffs/Makefile
|
|
new file mode 100644
|
|
index 0000000..615c2b2
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/Makefile
|
|
@@ -0,0 +1,18 @@
|
|
+#
|
|
+# Makefile for the Linux msdos filesystem routines.
|
|
+#
|
|
+# Note! Dependencies are done automagically by 'make dep', which also
|
|
+# removes any old dependencies. DON'T put your own dependencies here
|
|
+# unless it's something special (ie not a .c file).
|
|
+#
|
|
+# Note 2! The CFLAGS definitions are now in the main makefile.
|
|
+
|
|
+
|
|
+EXTRA_CFLAGS += -DCONFIG_YAFFS_YAFFS1 -DCONFIG_YAFFS_YAFFS2
|
|
+
|
|
+
|
|
+obj-$(CONFIG_YAFFS_FS) += yaffs.o
|
|
+
|
|
+yaffs-y = yaffs_fs.o yaffs_guts.o yaffs_mtdif.o yaffs_tagscompat.o \
|
|
+ yaffs_packedtags2.o yaffs_mtdif2.o yaffs_tagsvalidity.o \
|
|
+ yaffs_ecc.o
|
|
diff --git a/fs/yaffs/devextras.h b/fs/yaffs/devextras.h
|
|
new file mode 100644
|
|
index 0000000..752c2cc
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/devextras.h
|
|
@@ -0,0 +1,271 @@
|
|
+/*
|
|
+ * 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 <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * $Id: devextras.h,v 1.1 2004/11/03 08:14:07 charles Exp $
|
|
+ *
|
|
+ */
|
|
+
|
|
+#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 <sys/stat.h>
|
|
+#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 <linux/types.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/stat.h>
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
+#if defined WIN32
|
|
+#undef new
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_ecc.c b/fs/yaffs/yaffs_ecc.c
|
|
new file mode 100644
|
|
index 0000000..166bcad
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_ecc.c
|
|
@@ -0,0 +1,287 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ *
|
|
+ * yaffs_ecc.c: ECC generation/correction algorithms.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Lesser General Public License
|
|
+ * version 2.1 as published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * This code implements the ECC algorithm used in SmartMedia.
|
|
+ *
|
|
+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
|
|
+ * The two unused bit are set to 1.
|
|
+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC
|
|
+ * blocks are used on a 512-byte NAND page.
|
|
+ *
|
|
+ */
|
|
+
|
|
+// Table generated by gen-ecc.c
|
|
+// Using a table means we do not have to calculate p1..p4 and p1'..p4'
|
|
+// for each byte of data. These are instead provided in a table in bits7..2.
|
|
+// Bit 0 of each entry indicates whether the entry has an odd or even parity, and therefore
|
|
+// this bytes influence on the line parity.
|
|
+
|
|
+const char *yaffs_ecc_c_version = "$Id: yaffs_ecc.c,v 1.4 2005/07/31 00:28:04 charles Exp $";
|
|
+
|
|
+#include "yportenv.h"
|
|
+
|
|
+#include "yaffs_ecc.h"
|
|
+
|
|
+static const unsigned char column_parity_table[] = {
|
|
+0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
|
|
+0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
|
|
+0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
|
|
+0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
|
|
+0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
|
|
+0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
|
|
+0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
|
|
+0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
|
|
+0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
|
|
+0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
|
|
+0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
|
|
+0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
|
|
+0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
|
|
+0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
|
|
+0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
|
|
+0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
|
|
+};
|
|
+
|
|
+
|
|
+static int yaffs_CountBits(unsigned char x)
|
|
+{
|
|
+ int r = 0;
|
|
+ while(x)
|
|
+ {
|
|
+ if(x & 1) r++;
|
|
+ x >>= 1;
|
|
+ }
|
|
+ return r;
|
|
+}
|
|
+
|
|
+static int yaffs_CountBits32(unsigned x)
|
|
+{
|
|
+ int r = 0;
|
|
+ while(x)
|
|
+ {
|
|
+ if(x & 1) r++;
|
|
+ x >>= 1;
|
|
+ }
|
|
+ return r;
|
|
+}
|
|
+
|
|
+
|
|
+void yaffs_ECCCalculate(const unsigned char *data,unsigned char *ecc)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ unsigned char col_parity = 0;
|
|
+ unsigned char line_parity = 0;
|
|
+ unsigned char line_parity_prime = 0;
|
|
+ unsigned char t;
|
|
+ unsigned char b;
|
|
+
|
|
+ for(i = 0; i < 256; i++)
|
|
+ {
|
|
+ b = column_parity_table[*data++];
|
|
+ col_parity ^= b;
|
|
+
|
|
+ if(b & 0x01) // odd number of bits in the byte
|
|
+ {
|
|
+ line_parity ^= i;
|
|
+ line_parity_prime ^= ~i;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ ecc[2] = (~col_parity) | 0x03;
|
|
+
|
|
+ t = 0;
|
|
+ if(line_parity & 0x80) t |= 0x80;
|
|
+ if(line_parity_prime & 0x80) t |= 0x40;
|
|
+ if(line_parity & 0x40) t |= 0x20;
|
|
+ if(line_parity_prime & 0x40) t |= 0x10;
|
|
+ if(line_parity & 0x20) t |= 0x08;
|
|
+ if(line_parity_prime & 0x20) t |= 0x04;
|
|
+ if(line_parity & 0x10) t |= 0x02;
|
|
+ if(line_parity_prime & 0x10) t |= 0x01;
|
|
+ ecc[1] = ~t;
|
|
+
|
|
+ t = 0;
|
|
+ if(line_parity & 0x08) t |= 0x80;
|
|
+ if(line_parity_prime & 0x08) t |= 0x40;
|
|
+ if(line_parity & 0x04) t |= 0x20;
|
|
+ if(line_parity_prime & 0x04) t |= 0x10;
|
|
+ if(line_parity & 0x02) t |= 0x08;
|
|
+ if(line_parity_prime & 0x02) t |= 0x04;
|
|
+ if(line_parity & 0x01) t |= 0x02;
|
|
+ if(line_parity_prime & 0x01) t |= 0x01;
|
|
+ ecc[0] = ~t;
|
|
+
|
|
+#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
|
|
+ // Swap the bytes into the wrong order
|
|
+ t = ecc[0];
|
|
+ ecc[0] = ecc[1];
|
|
+ ecc[1] = t;
|
|
+#endif
|
|
+}
|
|
+
|
|
+int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, const unsigned char *test_ecc)
|
|
+{
|
|
+ unsigned char d0, d1, d2; // deltas
|
|
+
|
|
+ d0 = read_ecc[0] ^ test_ecc[0];
|
|
+ d1 = read_ecc[1] ^ test_ecc[1];
|
|
+ d2 = read_ecc[2] ^ test_ecc[2];
|
|
+
|
|
+
|
|
+
|
|
+ if((d0 | d1 | d2) == 0)
|
|
+ {
|
|
+ // no error
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if( ((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
|
|
+ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
|
|
+ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54)
|
|
+ {
|
|
+ // Single bit (recoverable) error in data
|
|
+
|
|
+ unsigned byte;
|
|
+ unsigned bit;
|
|
+
|
|
+#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
|
|
+ // swap the bytes to correct for the wrong order
|
|
+ unsigned char t;
|
|
+
|
|
+ t = d0;
|
|
+ d0 = d1;
|
|
+ d1 = t;
|
|
+#endif
|
|
+
|
|
+ bit = byte = 0;
|
|
+
|
|
+
|
|
+ if(d1 & 0x80) byte |= 0x80;
|
|
+ if(d1 & 0x20) byte |= 0x40;
|
|
+ if(d1 & 0x08) byte |= 0x20;
|
|
+ if(d1 & 0x02) byte |= 0x10;
|
|
+ if(d0 & 0x80) byte |= 0x08;
|
|
+ if(d0 & 0x20) byte |= 0x04;
|
|
+ if(d0 & 0x08) byte |= 0x02;
|
|
+ if(d0 & 0x02) byte |= 0x01;
|
|
+
|
|
+ if(d2 & 0x80) bit |= 0x04;
|
|
+ if(d2 & 0x20) bit |= 0x02;
|
|
+ if(d2 & 0x08) bit |= 0x01;
|
|
+
|
|
+ data[byte] ^= (1 << bit);
|
|
+
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if((yaffs_CountBits(d0)+yaffs_CountBits(d1)+yaffs_CountBits(d2)) == 1)
|
|
+ {
|
|
+ // Reccoverable error in ecc
|
|
+
|
|
+ read_ecc[0] = test_ecc[0];
|
|
+ read_ecc[1] = test_ecc[1];
|
|
+ read_ecc[2] = test_ecc[2];
|
|
+
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ // Unrecoverable error
|
|
+
|
|
+ return -1;
|
|
+
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+void yaffs_ECCCalculateOther(const unsigned char *data,unsigned nBytes, yaffs_ECCOther *eccOther)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ unsigned char col_parity = 0;
|
|
+ unsigned line_parity = 0;
|
|
+ unsigned line_parity_prime = 0;
|
|
+ unsigned char b;
|
|
+
|
|
+ for(i = 0; i < nBytes; i++)
|
|
+ {
|
|
+ b = column_parity_table[*data++];
|
|
+ col_parity ^= b;
|
|
+
|
|
+ if(b & 0x01) // odd number of bits in the byte
|
|
+ {
|
|
+ line_parity ^= i;
|
|
+ line_parity_prime ^= ~i;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ eccOther->colParity = (col_parity >> 2) & 0x3f;
|
|
+ eccOther->lineParity = line_parity;
|
|
+ eccOther->lineParityPrime = line_parity_prime;
|
|
+}
|
|
+
|
|
+int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, yaffs_ECCOther *read_ecc, const yaffs_ECCOther *test_ecc)
|
|
+{
|
|
+ unsigned char cDelta; // column parity delta
|
|
+ unsigned lDelta; // line parity delta
|
|
+ unsigned lDeltaPrime; // line parity delta
|
|
+ unsigned bit;
|
|
+
|
|
+ cDelta = read_ecc->colParity ^ test_ecc->colParity;
|
|
+ lDelta = read_ecc->lineParity ^ test_ecc->lineParity;
|
|
+ lDeltaPrime = read_ecc->lineParityPrime ^ test_ecc->lineParityPrime;
|
|
+
|
|
+ if((cDelta | lDelta | lDeltaPrime) == 0)
|
|
+ {
|
|
+ // no error
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if( lDelta == ~lDeltaPrime &&
|
|
+ (((cDelta ^ (cDelta >> 1)) & 0x15) == 0x15)) // Not correct
|
|
+ {
|
|
+ // Single bit (recoverable) error in data
|
|
+
|
|
+ bit = 0;
|
|
+
|
|
+ if(cDelta & 0x20) bit |= 0x04;
|
|
+ if(cDelta & 0x08) bit |= 0x02;
|
|
+ if(cDelta & 0x02) bit |= 0x01;
|
|
+
|
|
+ data[lDelta] ^= (1 << bit);
|
|
+
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if((yaffs_CountBits32(lDelta) + yaffs_CountBits32(lDeltaPrime) + yaffs_CountBits(cDelta)) == 1)
|
|
+ {
|
|
+ // Reccoverable error in ecc
|
|
+
|
|
+ *read_ecc = *test_ecc;
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ // Unrecoverable error
|
|
+
|
|
+ return -1;
|
|
+
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_ecc.h b/fs/yaffs/yaffs_ecc.h
|
|
new file mode 100644
|
|
index 0000000..f96d707
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_ecc.h
|
|
@@ -0,0 +1,41 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ *
|
|
+ * yaffs_ecc.c: ECC generation/correction algorithms.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * 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 code implements the ECC algorithm used in SmartMedia.
|
|
+ *
|
|
+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
|
|
+ * The two unused bit are set to 1.
|
|
+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC
|
|
+ * blocks are used on a 512-byte NAND page.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __YAFFS_ECC_H__
|
|
+#define __YAFFS_ECC_H__
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ unsigned char colParity;
|
|
+ unsigned lineParity;
|
|
+ unsigned lineParityPrime;
|
|
+} yaffs_ECCOther;
|
|
+
|
|
+void yaffs_ECCCalculate(const unsigned char *data,unsigned char *ecc);
|
|
+int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, const unsigned char *test_ecc);
|
|
+
|
|
+void yaffs_ECCCalculateOther(const unsigned char *data,unsigned nBytes, yaffs_ECCOther *ecc);
|
|
+int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, yaffs_ECCOther *read_ecc, const yaffs_ECCOther *test_ecc);
|
|
+#endif
|
|
diff --git a/fs/yaffs/yaffs_fs.c b/fs/yaffs/yaffs_fs.c
|
|
new file mode 100644
|
|
index 0000000..717f41a
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_fs.c
|
|
@@ -0,0 +1,1727 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ * yaffs_fs.c
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * 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 is the file system front-end to YAFFS that hooks it up to
|
|
+ * the VFS.
|
|
+ *
|
|
+ * Special notes:
|
|
+ * >> 2.4: sb->u.generic_sbp points to the yaffs_Device associated with this superblock
|
|
+ * >> 2.6: sb->s_fs_info points to the yaffs_Device associated with this superblock
|
|
+ * >> inode->u.generic_ip points to the associated yaffs_Object.
|
|
+ *
|
|
+ *
|
|
+ * Acknowledgements:
|
|
+ * * Luc van OostenRyck for numerous patches.
|
|
+ * * Nick Bane for numerous patches.
|
|
+ * * Nick Bane for 2.5/2.6 integration.
|
|
+ * * Andras Toth for mknod rdev issue.
|
|
+ * * Michael Fischer for finding the problem with inode inconsistency.
|
|
+ * * Some code bodily lifted from JFFS2.
|
|
+ */
|
|
+
|
|
+
|
|
+const char *yaffs_fs_c_version = "$Id: yaffs_fs.c,v 1.27 2005/08/04 22:47:36 luc Exp $";
|
|
+extern const char *yaffs_guts_c_version;
|
|
+
|
|
+
|
|
+#include <linux/config.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/smp_lock.h>
|
|
+#include <linux/pagemap.h>
|
|
+#include <linux/mtd/mtd.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/string.h>
|
|
+
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+
|
|
+#include <linux/statfs.h> /* Added NCB 15-8-2003 */
|
|
+#include <asm/statfs.h>
|
|
+#define UnlockPage(p) unlock_page(p)
|
|
+#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags)
|
|
+#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) // FIXME: use sb->s_id instead ?
|
|
+
|
|
+#else
|
|
+
|
|
+#include <linux/locks.h>
|
|
+#define BDEVNAME_SIZE 0
|
|
+#define yaffs_devname(sb, buf) kdevname(sb->s_dev)
|
|
+
|
|
+#endif
|
|
+
|
|
+#include <asm/uaccess.h>
|
|
+
|
|
+#include "yportenv.h"
|
|
+#include "yaffs_guts.h"
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | YAFFS_TRACE_BAD_BLOCKS;
|
|
+//unsigned yaffs_traceMask = 0xFFFFFFFF;
|
|
+
|
|
+
|
|
+#ifdef CONFIG_YAFFS_YAFFS1
|
|
+#include <linux/mtd/mtd.h>
|
|
+#include "yaffs_mtdif.h"
|
|
+#include "yaffs_mtdif2.h"
|
|
+#endif //CONFIG_YAFFS_YAFFS1
|
|
+
|
|
+//#define T(x) printk x
|
|
+
|
|
+
|
|
+
|
|
+#define yaffs_InodeToObject(iptr) ((yaffs_Object *)((iptr)->u.generic_ip))
|
|
+#define yaffs_DentryToObject(dptr) yaffs_InodeToObject((dptr)->d_inode)
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+#define yaffs_SuperToDevice(sb) ((yaffs_Device *)sb->s_fs_info)
|
|
+#else
|
|
+#define yaffs_SuperToDevice(sb) ((yaffs_Device *)sb->u.generic_sbp)
|
|
+#endif
|
|
+
|
|
+
|
|
+static void yaffs_put_super(struct super_block *sb);
|
|
+
|
|
+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, loff_t *pos);
|
|
+
|
|
+static int yaffs_file_flush(struct file* file);
|
|
+
|
|
+static int yaffs_sync_object(struct file * file, struct dentry *dentry, int datasync);
|
|
+
|
|
+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir);
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *n);
|
|
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *n);
|
|
+#else
|
|
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode);
|
|
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry);
|
|
+#endif
|
|
+static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry);
|
|
+static int yaffs_unlink(struct inode * dir, struct dentry *dentry);
|
|
+static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname);
|
|
+static int yaffs_mkdir(struct inode * dir, struct dentry * dentry, int mode);
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
|
|
+#else
|
|
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev);
|
|
+#endif
|
|
+static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry);
|
|
+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr);
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf);
|
|
+#else
|
|
+static int yaffs_statfs(struct super_block *sb, struct statfs *buf);
|
|
+#endif
|
|
+static void yaffs_read_inode (struct inode *inode);
|
|
+
|
|
+static void yaffs_put_inode (struct inode *inode);
|
|
+static void yaffs_delete_inode(struct inode *);
|
|
+static void yaffs_clear_inode(struct inode *);
|
|
+
|
|
+static int yaffs_readpage(struct file *file, struct page * page);
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+static int yaffs_writepage(struct page *page, struct writeback_control *wbc);
|
|
+#else
|
|
+static int yaffs_writepage(struct page *page);
|
|
+#endif
|
|
+static int yaffs_prepare_write(struct file *f, struct page *pg, unsigned offset, unsigned to);
|
|
+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, unsigned to);
|
|
+
|
|
+static int yaffs_readlink(struct dentry *dentry, char __user *buffer, int buflen);
|
|
+static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd);
|
|
+
|
|
+
|
|
+
|
|
+static struct address_space_operations yaffs_file_address_operations = {
|
|
+ .readpage = yaffs_readpage,
|
|
+ .writepage = yaffs_writepage,
|
|
+ .prepare_write = yaffs_prepare_write,
|
|
+ .commit_write = yaffs_commit_write,
|
|
+};
|
|
+
|
|
+static struct file_operations yaffs_file_operations = {
|
|
+ .read = generic_file_read,
|
|
+ .write = generic_file_write,
|
|
+ .mmap = generic_file_mmap,
|
|
+ .flush = yaffs_file_flush,
|
|
+ .fsync = yaffs_sync_object,
|
|
+};
|
|
+
|
|
+static struct inode_operations yaffs_file_inode_operations = {
|
|
+ .setattr = yaffs_setattr,
|
|
+};
|
|
+
|
|
+static struct inode_operations yaffs_symlink_inode_operations = {
|
|
+ .readlink = yaffs_readlink,
|
|
+ .follow_link = yaffs_follow_link,
|
|
+ .setattr = yaffs_setattr,
|
|
+};
|
|
+
|
|
+static struct inode_operations yaffs_dir_inode_operations = {
|
|
+ .create = yaffs_create,
|
|
+ .lookup = yaffs_lookup,
|
|
+ .link = yaffs_link,
|
|
+ .unlink = yaffs_unlink,
|
|
+ .symlink = yaffs_symlink,
|
|
+ .mkdir = yaffs_mkdir,
|
|
+ .rmdir = yaffs_unlink,
|
|
+ .mknod = yaffs_mknod,
|
|
+ .rename = yaffs_rename,
|
|
+ .setattr = yaffs_setattr,
|
|
+};
|
|
+
|
|
+static struct file_operations yaffs_dir_operations = {
|
|
+ .read = generic_read_dir,
|
|
+ .readdir = yaffs_readdir,
|
|
+ .fsync = yaffs_sync_object,
|
|
+};
|
|
+
|
|
+static struct super_operations yaffs_super_ops = {
|
|
+ .statfs = yaffs_statfs,
|
|
+ .read_inode = yaffs_read_inode,
|
|
+ .put_inode = yaffs_put_inode,
|
|
+ .put_super = yaffs_put_super,
|
|
+ .delete_inode = yaffs_delete_inode,
|
|
+ .clear_inode = yaffs_clear_inode,
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
+static void yaffs_GrossLock(yaffs_Device *dev)
|
|
+{
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs locking\n"));
|
|
+
|
|
+ down(&dev->grossLock);
|
|
+}
|
|
+
|
|
+static void yaffs_GrossUnlock(yaffs_Device *dev)
|
|
+{
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs unlocking\n"));
|
|
+ up(&dev->grossLock);
|
|
+
|
|
+}
|
|
+
|
|
+static int yaffs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
|
|
+{
|
|
+ unsigned char *alias;
|
|
+ int ret;
|
|
+
|
|
+ yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
|
|
+
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ if(!alias)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = vfs_readlink(dentry, buffer, buflen, alias);
|
|
+ kfree(alias);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
|
|
+{
|
|
+ unsigned char *alias;
|
|
+ int ret;
|
|
+ yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
|
|
+
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ if(!alias)
|
|
+ ret = -ENOMEM;
|
|
+ else {
|
|
+ ret = vfs_follow_link(nd,alias);
|
|
+ kfree(alias);
|
|
+ }
|
|
+ return ERR_PTR (ret);
|
|
+}
|
|
+
|
|
+
|
|
+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Object *obj);
|
|
+
|
|
+/*
|
|
+ * Lookup is used to find objects in the fs
|
|
+ */
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+
|
|
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *n)
|
|
+#else
|
|
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry)
|
|
+#endif
|
|
+{
|
|
+ yaffs_Object *obj;
|
|
+ struct inode *inode = NULL; // NCB 2.5/2.6 needs NULL here
|
|
+
|
|
+ yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev;
|
|
+
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_lookup for %d:%s\n",yaffs_InodeToObject(dir)->objectId,dentry->d_name.name));
|
|
+
|
|
+ obj = yaffs_FindObjectByName(yaffs_InodeToObject(dir),dentry->d_name.name);
|
|
+
|
|
+ obj = yaffs_GetEquivalentObject(obj); // in case it was a hardlink
|
|
+
|
|
+
|
|
+
|
|
+ if(obj)
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_lookup found %d\n",obj->objectId));
|
|
+
|
|
+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode,0,obj);
|
|
+
|
|
+ if(inode)
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_loookup dentry \n"));
|
|
+/* #if 0 asserted by NCB for 2.5/6 compatability - falls through to d_add even if NULL inode */
|
|
+#if 0
|
|
+ //dget(dentry); // try to solve directory bug
|
|
+ d_add(dentry,inode);
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ // return dentry;
|
|
+ return NULL;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_lookup not found\n"));
|
|
+
|
|
+ }
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+/* added NCB for 2.5/6 compatability - forces add even if inode is NULL which creates dentry hash*/
|
|
+ d_add(dentry,inode);
|
|
+
|
|
+ return NULL;
|
|
+ // return (ERR_PTR(-EIO));
|
|
+
|
|
+}
|
|
+
|
|
+// For now put inode is just for debugging
|
|
+// Put inode is called when the inode **structure** is put.
|
|
+static void yaffs_put_inode(struct inode *inode)
|
|
+{
|
|
+ T(YAFFS_TRACE_OS,("yaffs_put_inode: ino %d, count %d\n",(int)inode->i_ino, atomic_read(&inode->i_count)));
|
|
+
|
|
+}
|
|
+
|
|
+// clear is called to tell the fs to release any per-inode data it holds
|
|
+static void yaffs_clear_inode(struct inode *inode)
|
|
+{
|
|
+ yaffs_Object *obj;
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+ obj = yaffs_InodeToObject(inode);
|
|
+
|
|
+ T(YAFFS_TRACE_OS,("yaffs_clear_inode: ino %d, count %d %s\n",(int)inode->i_ino, atomic_read(&inode->i_count),
|
|
+ obj ? "object exists" : "null object"));
|
|
+
|
|
+ if(obj)
|
|
+ {
|
|
+ dev = obj->myDev;
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ // Clear the association between the inode ant the yaffs_Object.
|
|
+ obj->myInode = NULL;
|
|
+ inode->u.generic_ip = NULL;
|
|
+
|
|
+ // If the object freeing was deferred, then the real free happens now.
|
|
+ // This should fix the inode inconsistency problem.
|
|
+
|
|
+ yaffs_HandleDeferedFree(obj);
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+ }
|
|
+
|
|
+
|
|
+}
|
|
+
|
|
+// delete is called when the link count is zero and the inode
|
|
+// is put (ie. nobody wants to know about it anymore, time to
|
|
+// delete the file).
|
|
+// NB Must call clear_inode()
|
|
+static void yaffs_delete_inode(struct inode *inode)
|
|
+{
|
|
+ yaffs_Object *obj = yaffs_InodeToObject(inode);
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,("yaffs_delete_inode: ino %d, count %d %s\n",(int)inode->i_ino, atomic_read(&inode->i_count),
|
|
+ obj ? "object exists" : "null object"));
|
|
+
|
|
+ if(obj)
|
|
+ {
|
|
+ dev = obj->myDev;
|
|
+ yaffs_GrossLock(dev);
|
|
+ yaffs_DeleteFile(obj);
|
|
+ yaffs_GrossUnlock(dev);
|
|
+ }
|
|
+ clear_inode(inode);
|
|
+}
|
|
+
|
|
+
|
|
+static int yaffs_file_flush(struct file* file)
|
|
+{
|
|
+ yaffs_Object *obj = yaffs_DentryToObject(file->f_dentry);
|
|
+
|
|
+ yaffs_Device *dev = obj->myDev;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_file_flush object %d (%s)\n",obj->objectId,
|
|
+ obj->dirty ? "dirty" : "clean"));
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ yaffs_FlushFile(obj,1);
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static int yaffs_readpage_nolock(struct file *f, struct page * pg)
|
|
+{
|
|
+ // Lifted from jffs2
|
|
+
|
|
+ yaffs_Object *obj;
|
|
+ unsigned char *pg_buf;
|
|
+ int ret;
|
|
+
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_readpage at %08x, size %08x\n",
|
|
+ (unsigned)(pg->index << PAGE_CACHE_SHIFT), (unsigned)PAGE_CACHE_SIZE));
|
|
+
|
|
+ obj = yaffs_DentryToObject(f->f_dentry);
|
|
+
|
|
+ dev = obj->myDev;
|
|
+
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+ BUG_ON(!PageLocked(pg));
|
|
+#else
|
|
+ if (!PageLocked(pg))
|
|
+ PAGE_BUG(pg);
|
|
+#endif
|
|
+
|
|
+ pg_buf = kmap(pg);
|
|
+ /* FIXME: Can kmap fail? */
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ ret = yaffs_ReadDataFromFile(obj, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ if(ret >= 0) ret = 0;
|
|
+
|
|
+ if (ret) {
|
|
+ ClearPageUptodate(pg);
|
|
+ SetPageError(pg);
|
|
+ } else {
|
|
+ SetPageUptodate(pg);
|
|
+ ClearPageError(pg);
|
|
+ }
|
|
+
|
|
+ flush_dcache_page(pg);
|
|
+ kunmap(pg);
|
|
+
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_readpage done\n"));
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int yaffs_readpage_unlock(struct file *f, struct page *pg)
|
|
+{
|
|
+ int ret = yaffs_readpage_nolock(f,pg);
|
|
+ UnlockPage(pg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int yaffs_readpage(struct file *f, struct page * pg)
|
|
+{
|
|
+ return yaffs_readpage_unlock(f,pg);
|
|
+}
|
|
+
|
|
+// writepage inspired by/stolen from smbfs
|
|
+//
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+static int yaffs_writepage(struct page *page, struct writeback_control *wbc)
|
|
+#else
|
|
+static int yaffs_writepage(struct page *page)
|
|
+#endif
|
|
+{
|
|
+ struct address_space *mapping = page->mapping;
|
|
+ loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
|
|
+ struct inode *inode;
|
|
+ unsigned long end_index;
|
|
+ char *buffer;
|
|
+ yaffs_Object *obj;
|
|
+ int nWritten = 0;
|
|
+ unsigned nBytes;
|
|
+
|
|
+ if (!mapping)
|
|
+ BUG();
|
|
+ inode = mapping->host;
|
|
+ if (!inode)
|
|
+ BUG();
|
|
+
|
|
+ if (offset > inode->i_size)
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_writepage at %08x, inode size = %08x!!!\n", (unsigned)(page->index << PAGE_CACHE_SHIFT), (unsigned) inode->i_size));
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG" -> don't care!!\n"));
|
|
+ unlock_page(page);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ end_index = inode->i_size >> PAGE_CACHE_SHIFT;
|
|
+
|
|
+ /* easy case */
|
|
+ if (page->index < end_index)
|
|
+ {
|
|
+ nBytes = PAGE_CACHE_SIZE;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nBytes = inode->i_size & (PAGE_CACHE_SIZE-1);
|
|
+ }
|
|
+ // What's happening here?
|
|
+ ///* OK, are we completely out? */
|
|
+ //if (page->index >= end_index+1 || !offset)
|
|
+ // return -EIO;
|
|
+
|
|
+ get_page(page);
|
|
+
|
|
+
|
|
+ buffer = kmap(page);
|
|
+
|
|
+ obj = yaffs_InodeToObject(inode);
|
|
+ yaffs_GrossLock(obj->myDev);
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_writepage at %08x, size %08x\n", (unsigned)(page->index << PAGE_CACHE_SHIFT), nBytes));
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"writepag0: obj = %05x, ino = %05x\n", (int) obj->variant.fileVariant.fileSize, (int) inode->i_size));
|
|
+
|
|
+ nWritten = yaffs_WriteDataToFile(obj,buffer,page->index << PAGE_CACHE_SHIFT,nBytes,0);
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"writepag1: obj = %05x, ino = %05x\n", (int) obj->variant.fileVariant.fileSize, (int) inode->i_size));
|
|
+
|
|
+ yaffs_GrossUnlock(obj->myDev);
|
|
+
|
|
+ kunmap(page);
|
|
+ SetPageUptodate(page);
|
|
+ UnlockPage(page);
|
|
+ put_page(page);
|
|
+
|
|
+ return (nWritten == nBytes) ? 0 : -ENOSPC;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static int yaffs_prepare_write(struct file *f, struct page *pg, unsigned offset, unsigned to)
|
|
+{
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_prepair_write\n"));
|
|
+ if(!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE))
|
|
+ return yaffs_readpage_nolock(f,pg);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, unsigned to)
|
|
+{
|
|
+
|
|
+ void *addr = page_address(pg) + offset;
|
|
+ loff_t pos = (((loff_t)pg->index) << PAGE_CACHE_SHIFT) + offset;
|
|
+ int nBytes = to - offset;
|
|
+ int nWritten;
|
|
+
|
|
+ unsigned spos = pos;
|
|
+ unsigned saddr = (unsigned)addr;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_commit_write addr %x pos %x nBytes %d\n",saddr,spos,nBytes));
|
|
+
|
|
+ nWritten = yaffs_file_write(f,addr, nBytes, &pos);
|
|
+
|
|
+ if(nWritten != nBytes)
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_commit_write not same size nWritten %d nBytes %d\n",nWritten,nBytes));
|
|
+ SetPageError(pg);
|
|
+ ClearPageUptodate(pg);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ SetPageUptodate(pg);
|
|
+ }
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_commit_write returning %d\n",nWritten));
|
|
+
|
|
+ return nWritten;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object *obj)
|
|
+{
|
|
+ if (inode && obj)
|
|
+ {
|
|
+ inode->i_ino = obj->objectId;
|
|
+ inode->i_mode = obj->yst_mode;
|
|
+ inode->i_uid = obj->yst_uid;
|
|
+ inode->i_gid = obj->yst_gid;
|
|
+ inode->i_blksize = inode->i_sb->s_blocksize;
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+
|
|
+ inode->i_rdev = old_decode_dev(obj->yst_rdev);
|
|
+ inode->i_atime.tv_sec = (time_t)(obj->yst_atime);
|
|
+ inode->i_atime.tv_nsec = 0;
|
|
+ inode->i_mtime.tv_sec = (time_t)obj->yst_mtime;
|
|
+ inode->i_mtime.tv_nsec =0;
|
|
+ inode->i_ctime.tv_sec = (time_t)obj->yst_ctime;
|
|
+ inode->i_ctime.tv_nsec = 0;
|
|
+#else
|
|
+ inode->i_rdev = obj->yst_rdev;
|
|
+ inode->i_atime = obj->yst_atime;
|
|
+ inode->i_mtime = obj->yst_mtime;
|
|
+ inode->i_ctime = obj->yst_ctime;
|
|
+#endif
|
|
+ inode->i_size = yaffs_GetObjectFileLength(obj);
|
|
+ inode->i_blocks = (inode->i_size + 511) >> 9;
|
|
+
|
|
+ inode->i_nlink = yaffs_GetObjectLinkCount(obj);
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_FillInode mode %x uid %d gid %d size %d count %d\n",
|
|
+ inode->i_mode, inode->i_uid, inode->i_gid, (int)inode->i_size, atomic_read(&inode->i_count)));
|
|
+
|
|
+ switch (obj->yst_mode & S_IFMT)
|
|
+ {
|
|
+ default: // fifo, device or socket
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+ init_special_inode(inode, obj->yst_mode,old_decode_dev(obj->yst_rdev));
|
|
+#else
|
|
+ init_special_inode(inode, obj->yst_mode,(dev_t)(obj->yst_rdev));
|
|
+#endif
|
|
+ break;
|
|
+ case S_IFREG: // file
|
|
+ inode->i_op = &yaffs_file_inode_operations;
|
|
+ inode->i_fop = &yaffs_file_operations;
|
|
+ inode->i_mapping->a_ops = &yaffs_file_address_operations;
|
|
+ break;
|
|
+ case S_IFDIR: // directory
|
|
+ inode->i_op = &yaffs_dir_inode_operations;
|
|
+ inode->i_fop = &yaffs_dir_operations;
|
|
+ break;
|
|
+ case S_IFLNK: // symlink
|
|
+ inode->i_op = &yaffs_symlink_inode_operations;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+
|
|
+ inode->u.generic_ip = obj;
|
|
+ obj->myInode = inode;
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_FileInode invalid parameters\n"));
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Object *obj)
|
|
+{
|
|
+ struct inode * inode;
|
|
+
|
|
+ if(!sb)
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_get_inode for NULL super_block!!\n"));
|
|
+ return NULL;
|
|
+
|
|
+ }
|
|
+
|
|
+ if(!obj)
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_get_inode for NULL object!!\n"));
|
|
+ return NULL;
|
|
+
|
|
+ }
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_get_inode for object %d\n",obj->objectId));
|
|
+
|
|
+ inode = iget(sb,obj->objectId);
|
|
+
|
|
+ // NB Side effect: iget calls back to yaffs_read_inode().
|
|
+ // iget also increments the inode's i_count
|
|
+
|
|
+ return inode;
|
|
+}
|
|
+
|
|
+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, loff_t *pos)
|
|
+{
|
|
+ yaffs_Object *obj;
|
|
+ int nWritten,ipos;
|
|
+ struct inode *inode;
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+
|
|
+ obj = yaffs_DentryToObject(f->f_dentry);
|
|
+
|
|
+ dev = obj->myDev;
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ inode = f->f_dentry->d_inode;
|
|
+
|
|
+ if(!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND)
|
|
+ {
|
|
+ ipos = inode->i_size;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ipos = *pos;
|
|
+ }
|
|
+
|
|
+
|
|
+ if(!obj)
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_file_write: hey obj is null!\n"));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_file_write about to write writing %d bytes to object %d at %d\n",n,obj->objectId,ipos));
|
|
+ }
|
|
+
|
|
+ nWritten = yaffs_WriteDataToFile(obj,buf,ipos,n,0);
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_file_write writing %d bytes, %d written at %d\n",n,nWritten,ipos));
|
|
+ if(nWritten > 0)
|
|
+ {
|
|
+ ipos += nWritten;
|
|
+ *pos = ipos;
|
|
+ if(ipos > inode->i_size)
|
|
+ {
|
|
+ inode->i_size = ipos;
|
|
+ inode->i_blocks = (ipos + 511)>>9;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_file_write size updated to %d bytes, %d blocks\n",ipos,(int)(inode->i_blocks)));
|
|
+ }
|
|
+
|
|
+ }
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ return nWritten != n ? -ENOSPC : nWritten;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
|
|
+{
|
|
+ yaffs_Object *obj;
|
|
+ yaffs_Device *dev;
|
|
+ struct inode *inode = f->f_dentry->d_inode;
|
|
+ unsigned long offset, curoffs;
|
|
+ struct list_head *i;
|
|
+ yaffs_Object *l;
|
|
+
|
|
+ char name[YAFFS_MAX_NAME_LENGTH +1];
|
|
+
|
|
+ obj = yaffs_DentryToObject(f->f_dentry);
|
|
+ dev = obj->myDev;
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ offset = f->f_pos;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,("yaffs_readdir: starting at %d\n",(int)offset));
|
|
+
|
|
+ if(offset == 0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_readdir: entry . ino %d \n",(int)inode->i_ino));
|
|
+ if(filldir(dirent,".",1,offset,inode->i_ino,DT_DIR) < 0)
|
|
+ {
|
|
+ goto out;
|
|
+ }
|
|
+ offset++;
|
|
+ f->f_pos++;
|
|
+ }
|
|
+ if(offset == 1)
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_readdir: entry .. ino %d \n",(int)f->f_dentry->d_parent->d_inode->i_ino));
|
|
+ if(filldir(dirent,"..",2,offset,f->f_dentry->d_parent->d_inode->i_ino,DT_DIR) < 0)
|
|
+ {
|
|
+ goto out;
|
|
+ }
|
|
+ offset++;
|
|
+ f->f_pos++;
|
|
+ }
|
|
+
|
|
+ curoffs = 1;
|
|
+
|
|
+ list_for_each(i,&obj->variant.directoryVariant.children)
|
|
+ {
|
|
+ curoffs++;
|
|
+ if(curoffs >= offset)
|
|
+ {
|
|
+ l = list_entry(i, yaffs_Object,siblings);
|
|
+
|
|
+ yaffs_GetObjectName(l,name,YAFFS_MAX_NAME_LENGTH+1);
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_readdir: %s inode %d\n",name,yaffs_GetObjectInode(l)));
|
|
+
|
|
+ if(filldir(dirent,
|
|
+ name,
|
|
+ strlen(name),
|
|
+ offset,
|
|
+ yaffs_GetObjectInode(l),
|
|
+ yaffs_GetObjectType(l))
|
|
+ < 0)
|
|
+ {
|
|
+ goto up_and_out;
|
|
+ }
|
|
+
|
|
+ offset++;
|
|
+ f->f_pos++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ up_and_out:
|
|
+ out:
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * File creation. Allocate an inode, and we're done..
|
|
+ */
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
|
|
+#else
|
|
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
|
|
+#endif
|
|
+{
|
|
+ struct inode *inode;
|
|
+
|
|
+ yaffs_Object *obj = NULL;
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+ yaffs_Object *parent = yaffs_InodeToObject(dir);
|
|
+
|
|
+ int error = -ENOSPC;
|
|
+ uid_t uid = current->fsuid;
|
|
+ gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
|
|
+
|
|
+ if(parent)
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: parent object %d type %d\n",
|
|
+ parent->objectId,parent->variantType));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: could not get parent object\n"));
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ T(YAFFS_TRACE_OS,("yaffs_mknod: making oject for %s, mode %x dev %x\n",
|
|
+ dentry->d_name.name, mode,rdev));
|
|
+
|
|
+ dev = parent->myDev;
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ switch (mode & S_IFMT)
|
|
+ {
|
|
+ default:
|
|
+ // Special (socket, fifo, device...)
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: making special\n"));
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+ obj = yaffs_MknodSpecial(parent,dentry->d_name.name,mode,uid, gid,old_encode_dev(rdev));
|
|
+#else
|
|
+ obj = yaffs_MknodSpecial(parent,dentry->d_name.name,mode,uid, gid,rdev);
|
|
+#endif
|
|
+ break;
|
|
+ case S_IFREG: // file
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: making file\n"));
|
|
+ obj = yaffs_MknodFile(parent,dentry->d_name.name,mode,uid, gid);
|
|
+ break;
|
|
+ case S_IFDIR: // directory
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: making directory\n"));
|
|
+ obj = yaffs_MknodDirectory(parent,dentry->d_name.name,mode,uid, gid);
|
|
+ break;
|
|
+ case S_IFLNK: // symlink
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: making file\n"));
|
|
+ obj = NULL; // Do we ever get here?
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if(obj)
|
|
+ {
|
|
+ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj);
|
|
+ d_instantiate(dentry, inode);
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod created object %d count = %d\n",obj->objectId,atomic_read(&inode->i_count)));
|
|
+ error = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod failed making object\n"));
|
|
+ error = -ENOMEM;
|
|
+ }
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static int yaffs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
|
|
+{
|
|
+ int retVal;
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mkdir\n"));
|
|
+ retVal = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0);
|
|
+#if 0
|
|
+ // attempt to fix dir bug - didn't work
|
|
+ if(!retVal)
|
|
+ {
|
|
+ dget(dentry);
|
|
+ }
|
|
+#endif
|
|
+ return retVal;
|
|
+}
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *n)
|
|
+#else
|
|
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode)
|
|
+#endif
|
|
+{
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_create\n"));
|
|
+ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0);
|
|
+}
|
|
+
|
|
+
|
|
+static int yaffs_unlink(struct inode * dir, struct dentry *dentry)
|
|
+{
|
|
+ int retVal;
|
|
+
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_unlink %d:%s\n",(int)(dir->i_ino),dentry->d_name.name));
|
|
+
|
|
+ dev = yaffs_InodeToObject(dir)->myDev;
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+
|
|
+ retVal = yaffs_Unlink(yaffs_InodeToObject(dir),dentry->d_name.name);
|
|
+
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ if( retVal == YAFFS_OK)
|
|
+ {
|
|
+ dentry->d_inode->i_nlink--;
|
|
+ mark_inode_dirty(dentry->d_inode);
|
|
+ return 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return -ENOTEMPTY;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Create a link...
|
|
+ */
|
|
+static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry)
|
|
+{
|
|
+ struct inode *inode = old_dentry->d_inode;
|
|
+ yaffs_Object *obj = NULL;
|
|
+ yaffs_Object *link=NULL;
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_link\n"));
|
|
+
|
|
+ obj = yaffs_InodeToObject(inode);
|
|
+ dev = obj->myDev;
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ if (!S_ISDIR(inode->i_mode)) // Don't link directories
|
|
+ {
|
|
+ link = yaffs_Link(yaffs_InodeToObject(dir),dentry->d_name.name,obj);
|
|
+ }
|
|
+
|
|
+
|
|
+ if(link)
|
|
+ {
|
|
+ old_dentry->d_inode->i_nlink = yaffs_GetObjectLinkCount(obj);
|
|
+ d_instantiate(dentry, old_dentry->d_inode);
|
|
+ atomic_inc(&old_dentry->d_inode->i_count);
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_link link count %d i_count %d\n",
|
|
+ old_dentry->d_inode->i_nlink,atomic_read(&old_dentry->d_inode->i_count)));
|
|
+
|
|
+ }
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+
|
|
+ if(link)
|
|
+ {
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+
|
|
+ return -EPERM;
|
|
+}
|
|
+
|
|
+
|
|
+static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
|
|
+{
|
|
+ yaffs_Object *obj;
|
|
+ yaffs_Device *dev;
|
|
+ uid_t uid = current->fsuid;
|
|
+ gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_symlink\n"));
|
|
+
|
|
+ dev = yaffs_InodeToObject(dir)->myDev;
|
|
+ yaffs_GrossLock(dev);
|
|
+ obj = yaffs_MknodSymLink(yaffs_InodeToObject(dir), dentry->d_name.name,
|
|
+ S_IFLNK | S_IRWXUGO, uid, gid,
|
|
+ symname);
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ if(obj)
|
|
+ {
|
|
+
|
|
+ struct inode* inode;
|
|
+
|
|
+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj);
|
|
+ d_instantiate(dentry, inode);
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"symlink created OK\n"));
|
|
+ return 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"symlink not created\n"));
|
|
+
|
|
+ }
|
|
+
|
|
+ return -ENOMEM;
|
|
+}
|
|
+
|
|
+static int yaffs_sync_object(struct file * file, struct dentry *dentry, int datasync)
|
|
+{
|
|
+
|
|
+ yaffs_Object *obj;
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+ obj = yaffs_DentryToObject(dentry);
|
|
+
|
|
+ dev = obj->myDev;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_sync_object\n"));
|
|
+ yaffs_GrossLock(dev);
|
|
+ yaffs_FlushFile(obj,1);
|
|
+ yaffs_GrossUnlock(dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * The VFS layer already does all the dentry stuff for rename.
|
|
+ *
|
|
+ * NB: POSIX says you can rename an object over an old object of the same name
|
|
+ */
|
|
+static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry)
|
|
+{
|
|
+ yaffs_Device *dev;
|
|
+ int retVal = YAFFS_FAIL;
|
|
+ int removed = 0;
|
|
+ yaffs_Object *target;
|
|
+
|
|
+ dev = yaffs_InodeToObject(old_dir)->myDev;
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ // Check if the target is an existing directory that is not empty.
|
|
+ target = yaffs_FindObjectByName(yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
|
|
+
|
|
+ if(target &&
|
|
+ target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
|
|
+ !list_empty(&target->variant.directoryVariant.children))
|
|
+ {
|
|
+ retVal = YAFFS_FAIL;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+
|
|
+ // Unlink the target if it exists
|
|
+ removed = yaffs_Unlink(yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
|
|
+
|
|
+
|
|
+ retVal = yaffs_RenameObject(yaffs_InodeToObject(old_dir),old_dentry->d_name.name,
|
|
+ yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
|
|
+
|
|
+ }
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ if(retVal == YAFFS_OK)
|
|
+ {
|
|
+ if(removed == YAFFS_OK)
|
|
+ {
|
|
+ new_dentry->d_inode->i_nlink--;
|
|
+ mark_inode_dirty(new_dentry->d_inode);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return -ENOTEMPTY;
|
|
+ }
|
|
+
|
|
+
|
|
+}
|
|
+
|
|
+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
+{
|
|
+ struct inode *inode = dentry->d_inode;
|
|
+ int error;
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_setattr of object %d\n",yaffs_InodeToObject(inode)->objectId));
|
|
+
|
|
+ if((error = inode_change_ok(inode,attr)) == 0)
|
|
+ {
|
|
+
|
|
+ dev = yaffs_InodeToObject(inode)->myDev;
|
|
+ yaffs_GrossLock(dev);
|
|
+ if(yaffs_SetAttributes(yaffs_InodeToObject(inode),attr) == YAFFS_OK)
|
|
+ {
|
|
+ error = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ error = -EPERM;
|
|
+ }
|
|
+ yaffs_GrossUnlock(dev);
|
|
+ if (!error)
|
|
+ error = inode_setattr(inode,attr);
|
|
+ }
|
|
+ return error;
|
|
+}
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf)
|
|
+#else
|
|
+static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
|
|
+#endif
|
|
+{
|
|
+
|
|
+
|
|
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_statfs\n"));
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+
|
|
+ buf->f_type = YAFFS_MAGIC;
|
|
+ buf->f_bsize = sb->s_blocksize;
|
|
+ buf->f_namelen = 255;
|
|
+ if(sb->s_blocksize > dev->nBytesPerChunk)
|
|
+ {
|
|
+
|
|
+ buf->f_blocks = (dev->endBlock - dev->startBlock + 1) * dev->nChunksPerBlock/
|
|
+ (sb->s_blocksize/dev->nBytesPerChunk);
|
|
+ buf->f_bfree = yaffs_GetNumberOfFreeChunks(dev)/
|
|
+ (sb->s_blocksize/dev->nBytesPerChunk);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+
|
|
+ buf->f_blocks = (dev->endBlock - dev->startBlock + 1) * dev->nChunksPerBlock *
|
|
+ (dev->nBytesPerChunk/sb->s_blocksize);
|
|
+ buf->f_bfree = yaffs_GetNumberOfFreeChunks(dev) *
|
|
+ (dev->nBytesPerChunk/sb->s_blocksize);
|
|
+ }
|
|
+ buf->f_files = 0;
|
|
+ buf->f_ffree = 0;
|
|
+ buf->f_bavail = buf->f_bfree;
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void yaffs_read_inode (struct inode *inode)
|
|
+{
|
|
+ // NB This is called as a side effect of other functions and
|
|
+ // thus gross locking should always be in place already.
|
|
+
|
|
+ yaffs_Object *obj ;
|
|
+ yaffs_Device *dev = yaffs_SuperToDevice(inode->i_sb);
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_read_inode for %d\n",(int)inode->i_ino));
|
|
+
|
|
+ obj = yaffs_FindObjectByNumber(dev,inode->i_ino);
|
|
+
|
|
+ yaffs_FillInodeFromObject(inode,obj);
|
|
+
|
|
+}
|
|
+
|
|
+static LIST_HEAD(yaffs_dev_list);
|
|
+
|
|
+static void yaffs_put_super(struct super_block *sb)
|
|
+{
|
|
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+ if(dev->putSuperFunc)
|
|
+ {
|
|
+ dev->putSuperFunc(sb);
|
|
+ }
|
|
+ yaffs_Deinitialise(dev);
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ /* we assume this is protected by lock_kernel() in mount/umount */
|
|
+ list_del(&dev->devList);
|
|
+
|
|
+ kfree(dev);
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef CONFIG_YAFFS_YAFFS1
|
|
+
|
|
+static void yaffs_MTDPutSuper(struct super_block *sb)
|
|
+{
|
|
+
|
|
+ struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice;
|
|
+
|
|
+ if(mtd->sync)
|
|
+ {
|
|
+ mtd->sync(mtd);
|
|
+ }
|
|
+
|
|
+ put_mtd_device(mtd);
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+static struct super_block *yaffs_internal_read_super(int yaffsVersion, struct super_block * sb, void * data, int silent)
|
|
+{
|
|
+ int nBlocks;
|
|
+ struct inode * inode = NULL;
|
|
+ struct dentry * root;
|
|
+ yaffs_Device *dev = 0;
|
|
+ char devname_buf[BDEVNAME_SIZE+1];
|
|
+ struct mtd_info *mtd;
|
|
+ int err;
|
|
+
|
|
+ sb->s_magic = YAFFS_MAGIC;
|
|
+ sb->s_op = &yaffs_super_ops;
|
|
+
|
|
+ if(!sb)
|
|
+ printk(KERN_INFO"yaffs: sb is NULL\n");
|
|
+ else if(!sb->s_dev)
|
|
+ printk(KERN_INFO"yaffs: sb->s_dev is NULL\n");
|
|
+ else if(!yaffs_devname(sb, devname_buf))
|
|
+ printk(KERN_INFO"yaffs: devname is NULL\n");
|
|
+ else
|
|
+ printk(KERN_INFO"yaffs: dev is %d name is \"%s\"\n", sb->s_dev, yaffs_devname(sb, devname_buf));
|
|
+
|
|
+
|
|
+
|
|
+ sb->s_blocksize = PAGE_CACHE_SIZE;
|
|
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
|
+ T(YAFFS_TRACE_OS,("yaffs_read_super: Using yaffs%d\n",yaffsVersion));
|
|
+ T(YAFFS_TRACE_OS,("yaffs_read_super: block size %d\n", (int)(sb->s_blocksize)));
|
|
+
|
|
+#ifdef CONFIG_YAFFS_DISABLE_WRITE_VERIFY
|
|
+ T(YAFFS_TRACE_OS,("yaffs: Write verification disabled. All guarantees null and void\n"));
|
|
+#endif
|
|
+
|
|
+
|
|
+ T(YAFFS_TRACE_ALWAYS,("yaffs: Attempting MTD mount on %u.%u, \"%s\"\n",
|
|
+ MAJOR(sb->s_dev),MINOR(sb->s_dev), yaffs_devname(sb, devname_buf)));
|
|
+
|
|
+ // Check it's an mtd device.....
|
|
+ if(MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR)
|
|
+ {
|
|
+ return NULL; // This isn't an mtd device
|
|
+ }
|
|
+
|
|
+ // Get the device
|
|
+ mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
|
|
+ if (!mtd)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev)));
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ // Check it's NAND
|
|
+ if(mtd->type != MTD_NANDFLASH)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device is not NAND it's type %d\n", mtd->type));
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ T(YAFFS_TRACE_OS,(" erase %p\n",mtd->erase));
|
|
+ T(YAFFS_TRACE_OS,(" read %p\n",mtd->read));
|
|
+ T(YAFFS_TRACE_OS,(" write %p\n",mtd->write));
|
|
+ T(YAFFS_TRACE_OS,(" readoob %p\n",mtd->read_oob));
|
|
+ T(YAFFS_TRACE_OS,(" writeoob %p\n",mtd->write_oob));
|
|
+// T(YAFFS_TRACE_OS,(" block_isbad %p\n",mtd->block_isbad));
|
|
+// T(YAFFS_TRACE_OS,(" block_markbad %p\n",mtd->block_markbad));
|
|
+ T(YAFFS_TRACE_OS,(" oobblock %d\n",mtd->oobblock));
|
|
+ T(YAFFS_TRACE_OS,(" oobsize %d\n",mtd->oobsize));
|
|
+ T(YAFFS_TRACE_OS,(" erasesize %d\n",mtd->erasesize));
|
|
+ T(YAFFS_TRACE_OS,(" size %d\n",mtd->size));
|
|
+
|
|
+ if(yaffsVersion == 2)
|
|
+ {
|
|
+ // Check for version 2 style functions
|
|
+ if(!mtd->erase ||
|
|
+// !mtd->block_isbad ||
|
|
+// !mtd->block_markbad ||
|
|
+ !mtd->read ||
|
|
+ !mtd->write ||
|
|
+ !mtd->write_ecc ||
|
|
+ !mtd->read_ecc ||
|
|
+ !mtd->read_oob ||
|
|
+ !mtd->write_oob )
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device does not support required functions\n"));;
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if(mtd->oobblock < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
|
|
+ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device does not support have the right page sizes\n"));
|
|
+ return NULL;
|
|
+ } }
|
|
+ else
|
|
+ {
|
|
+ // Check for V1 style functions
|
|
+ if(!mtd->erase ||
|
|
+ !mtd->read ||
|
|
+ !mtd->write ||
|
|
+ !mtd->write_ecc ||
|
|
+ !mtd->read_ecc ||
|
|
+ !mtd->read_oob ||
|
|
+ !mtd->write_oob )
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device does not support required functions\n"));;
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if(mtd->oobblock != YAFFS_BYTES_PER_CHUNK ||
|
|
+ mtd->oobsize != YAFFS_BYTES_PER_SPARE)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device does not support have the right page sizes\n"));
|
|
+ return NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ // OK, so if we got here, we have an MTD that's NAND and looks
|
|
+ // like it has the right capabilities
|
|
+ // Set the yaffs_Device up for mtd
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+ sb->s_fs_info = dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
|
|
+#else
|
|
+ sb->u.generic_sbp = dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
|
|
+#endif
|
|
+ if(!dev)
|
|
+ {
|
|
+ // Deep shit could not allocate device structure
|
|
+ T(YAFFS_TRACE_ALWAYS,("yaffs_read_super: Failed trying to allocate yaffs_Device. \n"));
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ memset(dev,0,sizeof(yaffs_Device));
|
|
+ dev->genericDevice = mtd;
|
|
+ dev->name = mtd->name;
|
|
+
|
|
+ // Set up the memory size parameters....
|
|
+
|
|
+ nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
|
|
+ dev->startBlock = 0;
|
|
+ dev->endBlock = nBlocks - 1;
|
|
+ dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
|
|
+ dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
|
|
+ dev->nReservedBlocks = 5;
|
|
+ dev->nShortOpCaches = 10; // Enable short op caching
|
|
+
|
|
+
|
|
+ // ... and the functions.
|
|
+ if(yaffsVersion == 2)
|
|
+ {
|
|
+ dev->writeChunkWithTagsToNAND = nandmtd2_WriteChunkWithTagsToNAND;
|
|
+ dev->readChunkWithTagsFromNAND = nandmtd2_ReadChunkWithTagsFromNAND;
|
|
+ dev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad;
|
|
+ dev->queryNANDBlock = nandmtd2_QueryNANDBlock;
|
|
+ dev->spareBuffer = YMALLOC(mtd->oobsize);
|
|
+ dev->isYaffs2 = 1;
|
|
+ dev->nBytesPerChunk = mtd->oobblock;
|
|
+ dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock;
|
|
+ nBlocks = mtd->size / mtd->erasesize;
|
|
+ dev->startBlock = 0;
|
|
+ dev->endBlock = nBlocks - 1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ dev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
|
|
+ dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND;
|
|
+ dev->isYaffs2 = 0;
|
|
+ }
|
|
+ // ... and common functions
|
|
+ dev->eraseBlockInNAND = nandmtd_EraseBlockInNAND;
|
|
+ dev->initialiseNAND = nandmtd_InitialiseNAND;
|
|
+
|
|
+ dev->putSuperFunc = yaffs_MTDPutSuper;
|
|
+
|
|
+#ifndef CONFIG_YAFFS_DOES_ECC
|
|
+ dev->useNANDECC = 1;
|
|
+#endif
|
|
+
|
|
+ /* we assume this is protected by lock_kernel() in mount/umount */
|
|
+ list_add_tail(&dev->devList, &yaffs_dev_list);
|
|
+
|
|
+ init_MUTEX(&dev->grossLock);
|
|
+
|
|
+
|
|
+ yaffs_GrossLock(dev);
|
|
+
|
|
+ err = yaffs_GutsInitialise(dev);
|
|
+
|
|
+ T(YAFFS_TRACE_OS,("yaffs_read_super: guts initialised %s\n", (err == YAFFS_OK) ? "OK" : "FAILED"));
|
|
+
|
|
+ // Create root inode
|
|
+ if(err == YAFFS_OK)
|
|
+ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0,yaffs_Root(dev));
|
|
+
|
|
+ yaffs_GrossUnlock(dev);
|
|
+
|
|
+ if (!inode)
|
|
+ return NULL;
|
|
+
|
|
+// added NCB
|
|
+ inode->i_op = & yaffs_dir_inode_operations;
|
|
+ inode->i_fop = & yaffs_dir_operations;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,("yaffs_read_super: got root inode\n"));
|
|
+
|
|
+
|
|
+ root = d_alloc_root(inode);
|
|
+
|
|
+ T(YAFFS_TRACE_OS,("yaffs_read_super: d_alloc_root done\n"));
|
|
+
|
|
+ if (!root) {
|
|
+ iput(inode);
|
|
+ return NULL;
|
|
+ }
|
|
+ sb->s_root = root;
|
|
+
|
|
+ T(YAFFS_TRACE_OS,("yaffs_read_super: done\n"));
|
|
+ return sb;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+#ifdef CONFIG_YAFFS_YAFFS1
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+static int yaffs_internal_read_super_mtd(struct super_block * sb, void * data, int silent)
|
|
+{
|
|
+ return yaffs_internal_read_super(1,sb,data,silent) ? 0 : -1;
|
|
+}
|
|
+
|
|
+static struct super_block *yaffs_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data)
|
|
+{
|
|
+
|
|
+ return get_sb_bdev(fs, flags, dev_name, data, yaffs_internal_read_super_mtd);
|
|
+}
|
|
+
|
|
+static struct file_system_type yaffs_fs_type = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .name = "yaffs",
|
|
+ .get_sb = yaffs_read_super,
|
|
+ .kill_sb = kill_block_super,
|
|
+ .fs_flags = FS_REQUIRES_DEV,
|
|
+};
|
|
+#else
|
|
+static struct super_block *yaffs_read_super(struct super_block * sb, void * data, int silent)
|
|
+{
|
|
+ return yaffs_internal_read_super(1,sb,data,silent);
|
|
+}
|
|
+
|
|
+static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
|
|
+#endif
|
|
+
|
|
+#endif // CONFIG_YAFFS_YAFFS1
|
|
+
|
|
+#ifdef CONFIG_YAFFS_YAFFS2
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+static int yaffs2_internal_read_super_mtd(struct super_block * sb, void * data, int silent)
|
|
+{
|
|
+ return yaffs_internal_read_super(2,sb,data,silent) ? 0 : -1;
|
|
+}
|
|
+
|
|
+static struct super_block *yaffs2_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data)
|
|
+{
|
|
+
|
|
+ return get_sb_bdev(fs, flags, dev_name, data, yaffs2_internal_read_super_mtd);
|
|
+}
|
|
+
|
|
+static struct file_system_type yaffs2_fs_type = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .name = "yaffs2",
|
|
+ .get_sb = yaffs2_read_super,
|
|
+ .kill_sb = kill_block_super,
|
|
+ .fs_flags = FS_REQUIRES_DEV,
|
|
+};
|
|
+#else
|
|
+static struct super_block *yaffs2_read_super(struct super_block * sb, void * data, int silent)
|
|
+{
|
|
+ return yaffs_internal_read_super(2,sb,data,silent);
|
|
+}
|
|
+
|
|
+static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, FS_REQUIRES_DEV);
|
|
+#endif
|
|
+
|
|
+#endif // CONFIG_YAFFS_YAFFS2
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+static struct proc_dir_entry *my_proc_entry;
|
|
+
|
|
+static char * yaffs_dump_dev(char *buf,yaffs_Device *dev)
|
|
+{
|
|
+ buf +=sprintf(buf,"startBlock......... %d\n",dev->startBlock);
|
|
+ buf +=sprintf(buf,"endBlock........... %d\n",dev->endBlock);
|
|
+ buf +=sprintf(buf,"chunkGroupBits..... %d\n",dev->chunkGroupBits);
|
|
+ buf +=sprintf(buf,"chunkGroupSize..... %d\n",dev->chunkGroupSize);
|
|
+ buf +=sprintf(buf,"nErasedBlocks...... %d\n",dev->nErasedBlocks);
|
|
+ buf +=sprintf(buf,"nTnodesCreated..... %d\n",dev->nTnodesCreated);
|
|
+ buf +=sprintf(buf,"nFreeTnodes........ %d\n",dev->nFreeTnodes);
|
|
+ buf +=sprintf(buf,"nObjectsCreated.... %d\n",dev->nObjectsCreated);
|
|
+ buf +=sprintf(buf,"nFreeObjects....... %d\n",dev->nFreeObjects);
|
|
+ buf +=sprintf(buf,"nFreeChunks........ %d\n",dev->nFreeChunks);
|
|
+ buf +=sprintf(buf,"nPageWrites........ %d\n",dev->nPageWrites);
|
|
+ buf +=sprintf(buf,"nPageReads......... %d\n",dev->nPageReads);
|
|
+ buf +=sprintf(buf,"nBlockErasures..... %d\n",dev->nBlockErasures);
|
|
+ buf +=sprintf(buf,"nGCCopies.......... %d\n",dev->nGCCopies);
|
|
+ buf +=sprintf(buf,"garbageCollections. %d\n",dev->garbageCollections);
|
|
+ buf +=sprintf(buf,"passiveGCs......... %d\n",dev->passiveGarbageCollections);
|
|
+ buf +=sprintf(buf,"nRetriedWrites..... %d\n",dev->nRetriedWrites);
|
|
+ buf +=sprintf(buf,"nRetireBlocks...... %d\n",dev->nRetiredBlocks);
|
|
+ buf +=sprintf(buf,"eccFixed........... %d\n",dev->eccFixed);
|
|
+ buf +=sprintf(buf,"eccUnfixed......... %d\n",dev->eccUnfixed);
|
|
+ buf +=sprintf(buf,"tagsEccFixed....... %d\n",dev->tagsEccFixed);
|
|
+ buf +=sprintf(buf,"tagsEccUnfixed..... %d\n",dev->tagsEccUnfixed);
|
|
+ buf +=sprintf(buf,"cacheHits.......... %d\n",dev->cacheHits);
|
|
+ buf +=sprintf(buf,"nDeletedFiles...... %d\n",dev->nDeletedFiles);
|
|
+ buf +=sprintf(buf,"nUnlinkedFiles..... %d\n",dev->nUnlinkedFiles);
|
|
+ buf +=sprintf(buf,"nBackgroudDeletions %d\n",dev->nBackgroundDeletions);
|
|
+ buf +=sprintf(buf,"useNANDECC......... %d\n",dev->useNANDECC);
|
|
+ buf +=sprintf(buf,"isYaffs2........... %d\n",dev->isYaffs2);
|
|
+
|
|
+ return buf;
|
|
+}
|
|
+
|
|
+static int yaffs_proc_read(
|
|
+ char *page,
|
|
+ char **start,
|
|
+ off_t offset,
|
|
+ int count,
|
|
+ int *eof,
|
|
+ void *data
|
|
+ )
|
|
+{
|
|
+ struct list_head *item;
|
|
+ char *buf = page;
|
|
+ int step = offset;
|
|
+ int n = 0;
|
|
+
|
|
+ /* Get proc_file_read() to step 'offset' by one on each sucessive call.
|
|
+ * We use 'offset' (*ppos) to indicate where we are in devList.
|
|
+ * This also assumes the user has posted a read buffer large
|
|
+ * enough to hold the complete output; but that's life in /proc.
|
|
+ */
|
|
+
|
|
+ *(int *)start = 1;
|
|
+
|
|
+ /* Print header first */
|
|
+ if (step == 0) {
|
|
+ buf += sprintf(buf, "YAFFS built:" __DATE__ " "__TIME__
|
|
+ "\n%s\n%s\n", yaffs_fs_c_version, yaffs_guts_c_version);
|
|
+ }
|
|
+
|
|
+ /* hold lock_kernel while traversing yaffs_dev_list */
|
|
+ lock_kernel();
|
|
+
|
|
+ /* Locate and print the Nth entry. Order N-squared but N is small. */
|
|
+ list_for_each(item, &yaffs_dev_list) {
|
|
+ yaffs_Device *dev = list_entry(item, yaffs_Device, devList);
|
|
+ if (n < step) {
|
|
+ n++;
|
|
+ continue;
|
|
+ }
|
|
+ buf += sprintf(buf,"\nDevice %d \"%s\"\n", n, dev->name);
|
|
+ buf = yaffs_dump_dev(buf, dev);
|
|
+ break;
|
|
+ }
|
|
+ unlock_kernel();
|
|
+
|
|
+ return buf-page < count ? buf-page : count;
|
|
+}
|
|
+
|
|
+// Stuff to handle installation of file systems
|
|
+struct file_system_to_install
|
|
+{
|
|
+ struct file_system_type *fst;
|
|
+ int installed;
|
|
+};
|
|
+
|
|
+static struct file_system_to_install fs_to_install[] =
|
|
+{
|
|
+#ifdef CONFIG_YAFFS_YAFFS1
|
|
+ { &yaffs_fs_type,0},
|
|
+#endif
|
|
+#ifdef CONFIG_YAFFS_YAFFS2
|
|
+ { &yaffs2_fs_type,0},
|
|
+#endif
|
|
+ { NULL,0}
|
|
+};
|
|
+
|
|
+static int __init init_yaffs_fs(void)
|
|
+{
|
|
+ int error = 0;
|
|
+ struct file_system_to_install *fsinst;
|
|
+
|
|
+ T(YAFFS_TRACE_ALWAYS,("yaffs " __DATE__ " " __TIME__ " Installing. \n"));
|
|
+
|
|
+
|
|
+
|
|
+ /* Install the proc_fs entry */
|
|
+ my_proc_entry = create_proc_read_entry("yaffs",
|
|
+ S_IRUGO | S_IFREG,
|
|
+ &proc_root,
|
|
+ yaffs_proc_read,
|
|
+ NULL);
|
|
+ if(!my_proc_entry)
|
|
+ {
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+
|
|
+
|
|
+ // Now add the file system entries
|
|
+
|
|
+ fsinst = fs_to_install;
|
|
+
|
|
+ while(fsinst->fst && !error)
|
|
+ {
|
|
+ error = register_filesystem(fsinst->fst);
|
|
+ if(!error)
|
|
+ {
|
|
+ fsinst->installed = 1;
|
|
+ }
|
|
+ fsinst++;
|
|
+ }
|
|
+
|
|
+ // Any errors? uninstall
|
|
+ if(error)
|
|
+ {
|
|
+ fsinst = fs_to_install;
|
|
+
|
|
+ while(fsinst->fst)
|
|
+ {
|
|
+ if(fsinst->installed)
|
|
+ {
|
|
+ unregister_filesystem(fsinst->fst);
|
|
+ fsinst->installed = 0;
|
|
+ }
|
|
+ fsinst++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static void __exit exit_yaffs_fs(void)
|
|
+{
|
|
+
|
|
+ struct file_system_to_install *fsinst;
|
|
+
|
|
+ T(YAFFS_TRACE_ALWAYS,("yaffs " __DATE__ " " __TIME__ " removing. \n"));
|
|
+
|
|
+ remove_proc_entry("yaffs",&proc_root);
|
|
+
|
|
+ fsinst = fs_to_install;
|
|
+
|
|
+ while(fsinst->fst)
|
|
+ {
|
|
+ if(fsinst->installed)
|
|
+ {
|
|
+ unregister_filesystem(fsinst->fst);
|
|
+ fsinst->installed = 0;
|
|
+ }
|
|
+ fsinst++;
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+module_init(init_yaffs_fs)
|
|
+module_exit(exit_yaffs_fs)
|
|
+
|
|
+MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system");
|
|
+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002,2003,2004");
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_guts.c b/fs/yaffs/yaffs_guts.c
|
|
new file mode 100644
|
|
index 0000000..bf13f91
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_guts.c
|
|
@@ -0,0 +1,6346 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * 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
|
|
+
|
|
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.15 2005/08/02 04:24:22 charles Exp $";
|
|
+
|
|
+#include "yportenv.h"
|
|
+
|
|
+#include "yaffsinterface.h"
|
|
+#include "yaffs_guts.h"
|
|
+#include "yaffs_tagsvalidity.h"
|
|
+
|
|
+
|
|
+#include "yaffs_tagscompat.h"
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+void yfsd_LockYAFFS(BOOL fsLockOnly);
|
|
+void yfsd_UnlockYAFFS(BOOL fsLockOnly);
|
|
+#endif
|
|
+
|
|
+#define YAFFS_PASSIVE_GC_CHUNKS 2
|
|
+
|
|
+#if 0
|
|
+// Use Steven Hill's ECC struff instead
|
|
+// 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);
|
|
+#define yaffs_ECCCalculate(data,ecc) nand_calculate_ecc(data,ecc)
|
|
+#define yaffs_ECCCorrect(data,read_ecc,calc_ecc) nand_correct_ecc(data,read_ecc,calc_ecc)
|
|
+#else
|
|
+#include "yaffs_ecc.h"
|
|
+#endif
|
|
+
|
|
+#if 0
|
|
+// 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_countBitsTable[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
|
|
+};
|
|
+
|
|
+static int yaffs_CountBits(__u8 x)
|
|
+{
|
|
+ int retVal;
|
|
+ retVal = yaffs_countBitsTable[x];
|
|
+ return retVal;
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+#if 0
|
|
+// Stuff using yea olde tags
|
|
+static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr);
|
|
+static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);
|
|
+
|
|
+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);
|
|
+#else
|
|
+#endif
|
|
+
|
|
+// NAND access
|
|
+
|
|
+
|
|
+static Y_INLINE int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *buffer, yaffs_ExtendedTags *tags);
|
|
+static Y_INLINE int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *data, yaffs_ExtendedTags *tags);
|
|
+static Y_INLINE int yaffs_MarkBlockBad(yaffs_Device *dev, int blockNo);
|
|
+static Y_INLINE int yaffs_QueryInitialBlockState(yaffs_Device *dev,int blockNo, yaffs_BlockState *state,unsigned *sequenceNumber);
|
|
+// Local prototypes
|
|
+static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_ExtendedTags *tags, int useReserve);
|
|
+#if 0
|
|
+static int yaffs_CheckObjectHashSanity(yaffs_Device *dev);
|
|
+#endif
|
|
+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 YCHAR *name, int force,int isShrink, int shadows);
|
|
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);
|
|
+static int yaffs_CheckStructures(void);
|
|
+static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit);
|
|
+static int yaffs_DoGenericObjectDeletion(yaffs_Object *in);
|
|
+
|
|
+static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev,int blockNo);
|
|
+
|
|
+static __u8 *yaffs_GetTempBuffer(yaffs_Device *dev,int lineNo);
|
|
+static void yaffs_ReleaseTempBuffer(yaffs_Device *dev, __u8 *buffer, int lineNo);
|
|
+
|
|
+
|
|
+// Robustification (if it ever comes about...)
|
|
+static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND);
|
|
+#if 0
|
|
+static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND);
|
|
+#endif
|
|
+static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND);
|
|
+static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_ExtendedTags *tags);
|
|
+static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_ExtendedTags *tags);
|
|
+
|
|
+static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND);
|
|
+
|
|
+static int yaffs_UnlinkWorker(yaffs_Object *obj);
|
|
+static void yaffs_DestroyObject(yaffs_Object *obj);
|
|
+
|
|
+#if 0
|
|
+static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1,int dataSize);
|
|
+#endif
|
|
+
|
|
+static int yaffs_TagsMatch(const yaffs_ExtendedTags *tags, int objectId, int chunkInObject);
|
|
+
|
|
+
|
|
+loff_t yaffs_GetFileSize(yaffs_Object *obj);
|
|
+
|
|
+
|
|
+static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);
|
|
+
|
|
+static void yaffs_VerifyFreeChunks(yaffs_Device *dev);
|
|
+
|
|
+#ifdef YAFFS_PARANOID
|
|
+static int yaffs_CheckFileSanity(yaffs_Object *in);
|
|
+#else
|
|
+#define yaffs_CheckFileSanity(in)
|
|
+#endif
|
|
+
|
|
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in);
|
|
+static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId);
|
|
+
|
|
+static int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *buffer, yaffs_ExtendedTags *tags)
|
|
+{
|
|
+ chunkInNAND -= dev->chunkOffset;
|
|
+
|
|
+ if(dev->readChunkWithTagsFromNAND)
|
|
+ return dev->readChunkWithTagsFromNAND(dev,chunkInNAND,buffer,tags);
|
|
+ else
|
|
+ return yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev,chunkInNAND,buffer,tags);
|
|
+}
|
|
+
|
|
+static Y_INLINE int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_ExtendedTags *tags)
|
|
+{
|
|
+ chunkInNAND -= dev->chunkOffset;
|
|
+
|
|
+ if(tags)
|
|
+ {
|
|
+ tags->sequenceNumber = dev->sequenceNumber;
|
|
+ tags->chunkUsed = 1;
|
|
+ if(!yaffs_ValidateTags(tags))
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("Writing uninitialised tags" TENDSTR)));
|
|
+ YBUG();
|
|
+ }
|
|
+ T(YAFFS_TRACE_WRITE,(TSTR("Writing chunk %d tags %d %d"TENDSTR),chunkInNAND,tags->objectId,tags->chunkId));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("Writing with no tags" TENDSTR)));
|
|
+ YBUG();
|
|
+ }
|
|
+
|
|
+ if(dev->writeChunkWithTagsToNAND)
|
|
+ return dev->writeChunkWithTagsToNAND(dev,chunkInNAND,buffer,tags);
|
|
+ else
|
|
+ return yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(dev,chunkInNAND,buffer,tags);
|
|
+}
|
|
+
|
|
+static Y_INLINE int yaffs_MarkBlockBad(yaffs_Device *dev, int blockNo)
|
|
+{
|
|
+ blockNo -= dev->blockOffset;
|
|
+
|
|
+ if(dev->markNANDBlockBad)
|
|
+ return dev->markNANDBlockBad(dev,blockNo);
|
|
+ else
|
|
+ return yaffs_TagsCompatabilityMarkNANDBlockBad(dev,blockNo);
|
|
+}
|
|
+static Y_INLINE int yaffs_QueryInitialBlockState(yaffs_Device *dev,int blockNo, yaffs_BlockState *state, unsigned *sequenceNumber)
|
|
+{
|
|
+ blockNo -= dev->blockOffset;
|
|
+
|
|
+ if(dev->queryNANDBlock)
|
|
+ return dev->queryNANDBlock(dev,blockNo,state,sequenceNumber);
|
|
+ else
|
|
+ return yaffs_TagsCompatabilityQueryNANDBlock(dev,blockNo,state,sequenceNumber);
|
|
+}
|
|
+
|
|
+static int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
|
|
+{
|
|
+ int result;
|
|
+
|
|
+ blockInNAND -= dev->blockOffset;
|
|
+
|
|
+ dev->nBlockErasures++;
|
|
+ result = dev->eraseBlockInNAND(dev,blockInNAND);
|
|
+
|
|
+ if(!result)result = dev->eraseBlockInNAND(dev,blockInNAND); // If at first we don't succeed, try again *once*.
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
|
|
+{
|
|
+ return dev->initialiseNAND(dev);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+// Temporary buffer manipulations
|
|
+
|
|
+static __u8 *yaffs_GetTempBuffer(yaffs_Device *dev,int lineNo)
|
|
+{
|
|
+ int i,j;
|
|
+ for(i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
|
|
+ {
|
|
+ if(dev->tempBuffer[i].line == 0)
|
|
+ {
|
|
+ dev->tempBuffer[i].line = lineNo;
|
|
+ if((i+1) > dev->maxTemp)
|
|
+ {
|
|
+ dev->maxTemp = i + 1;
|
|
+ for(j = 0; j <= i; j++)
|
|
+ dev->tempBuffer[j].maxLine = dev->tempBuffer[j].line;
|
|
+ }
|
|
+
|
|
+ return dev->tempBuffer[i].buffer;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ T(YAFFS_TRACE_BUFFERS,(TSTR("Out of temp buffers at line %d, other held by lines:"),lineNo));
|
|
+ for(i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
|
|
+ {
|
|
+ T(YAFFS_TRACE_BUFFERS,(TSTR(" %d "),dev->tempBuffer[i].line));
|
|
+ }
|
|
+ T(YAFFS_TRACE_BUFFERS,(TSTR(" "TENDSTR)));
|
|
+
|
|
+ dev->unmanagedTempAllocations++;
|
|
+ // Get an unmanaged one
|
|
+ return YMALLOC(dev->nBytesPerChunk);
|
|
+
|
|
+
|
|
+}
|
|
+
|
|
+static void yaffs_ReleaseTempBuffer(yaffs_Device *dev, __u8 *buffer, int lineNo)
|
|
+{
|
|
+ int i;
|
|
+ for(i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
|
|
+ {
|
|
+ if(dev->tempBuffer[i].buffer == buffer)
|
|
+ {
|
|
+ dev->tempBuffer[i].line = 0;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(buffer)
|
|
+ {
|
|
+ // assume it is an unmanaged one.
|
|
+ T(YAFFS_TRACE_BUFFERS,(TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR),lineNo));
|
|
+ YFREE(buffer);
|
|
+ dev->unmanagedTempDeallocations++;
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+// Chunk bitmap manipulations
|
|
+
|
|
+static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device *dev, int blk)
|
|
+{
|
|
+ if(blk < dev->internalStartBlock || blk > dev->internalEndBlock)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR),blk));
|
|
+ YBUG();
|
|
+ }
|
|
+ return dev->chunkBits + (dev->chunkBitmapStride * (blk - dev->internalStartBlock));
|
|
+}
|
|
+
|
|
+static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device *dev,int blk)
|
|
+{
|
|
+ __u8 *blkBits = yaffs_BlockBits(dev,blk);
|
|
+
|
|
+ memset(blkBits,0,dev->chunkBitmapStride);
|
|
+}
|
|
+
|
|
+static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device *dev,int blk,int chunk)
|
|
+{
|
|
+ __u8 *blkBits = yaffs_BlockBits(dev,blk);
|
|
+
|
|
+ blkBits[chunk/8] &= ~ (1<<(chunk & 7));
|
|
+}
|
|
+
|
|
+static Y_INLINE void yaffs_SetChunkBit(yaffs_Device *dev,int blk,int chunk)
|
|
+{
|
|
+ __u8 *blkBits = yaffs_BlockBits(dev,blk);
|
|
+
|
|
+ blkBits[chunk/8] |= (1<<(chunk & 7));
|
|
+}
|
|
+
|
|
+static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device *dev,int blk,int chunk)
|
|
+{
|
|
+ __u8 *blkBits = yaffs_BlockBits(dev,blk);
|
|
+ return (blkBits[chunk/8] & (1<<(chunk & 7))) ? 1 :0;
|
|
+}
|
|
+
|
|
+static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device *dev,int blk)
|
|
+{
|
|
+ __u8 *blkBits = yaffs_BlockBits(dev,blk);
|
|
+ int i;
|
|
+ for(i = 0; i < dev->chunkBitmapStride; i++)
|
|
+ {
|
|
+ if(*blkBits) return 1;
|
|
+ blkBits++;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static Y_INLINE int 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;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+int yaffs_CheckFF(__u8 *buffer,int nBytes)
|
|
+{
|
|
+ //Horrible, slow implementation
|
|
+ while(nBytes--)
|
|
+ {
|
|
+ if(*buffer != 0xFF) return 0;
|
|
+ buffer++;
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)
|
|
+{
|
|
+
|
|
+ int retval = YAFFS_OK;
|
|
+ __u8 *data = yaffs_GetTempBuffer(dev,__LINE__);
|
|
+ yaffs_ExtendedTags tags;
|
|
+
|
|
+// NCB dev->readChunkWithTagsFromNAND(dev,chunkInNAND,data,&tags);
|
|
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunkInNAND,data,&tags);
|
|
+
|
|
+ if(!yaffs_CheckFF(data,dev->nBytesPerChunk) || tags.chunkUsed)
|
|
+ {
|
|
+ T(YAFFS_TRACE_NANDACCESS,(TSTR("Chunk %d not erased" TENDSTR),chunkInNAND));
|
|
+ retval = YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ yaffs_ReleaseTempBuffer(dev,data,__LINE__);
|
|
+
|
|
+ return retval;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+#if 1
|
|
+static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_ExtendedTags *tags,int useReserve)
|
|
+{
|
|
+ int chunk;
|
|
+
|
|
+ int writeOk = 1;
|
|
+ int attempts = 0;
|
|
+
|
|
+
|
|
+
|
|
+ do{
|
|
+ chunk = yaffs_AllocateChunk(dev,useReserve);
|
|
+
|
|
+ if(chunk >= 0)
|
|
+ {
|
|
+
|
|
+ // First check this chunk is erased...
|
|
+#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
|
|
+ writeOk = yaffs_CheckChunkErased(dev,chunk);
|
|
+#endif
|
|
+ if(!writeOk)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ writeOk = yaffs_WriteChunkWithTagsToNAND(dev,chunk,data,tags);
|
|
+ }
|
|
+ attempts++;
|
|
+
|
|
+ if(writeOk)
|
|
+ {
|
|
+ // Copy the data into the robustification buffer.
|
|
+ // NB We do this at the end to prevent duplicates in the case of a write error.
|
|
+ //Todo
|
|
+ yaffs_HandleWriteChunkOk(dev,chunk,data,tags);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ yaffs_HandleWriteChunkError(dev,chunk);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ } while(chunk >= 0 && ! writeOk);
|
|
+
|
|
+ if(attempts > 1)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));
|
|
+ dev->nRetriedWrites+= (attempts - 1);
|
|
+ }
|
|
+
|
|
+
|
|
+
|
|
+ return chunk;
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+///
|
|
+// Functions for robustisizing
|
|
+//
|
|
+//
|
|
+
|
|
+static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND)
|
|
+{
|
|
+
|
|
+ yaffs_MarkBlockBad(dev,blockInNAND);
|
|
+
|
|
+ yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD;
|
|
+
|
|
+ dev->nRetiredBlocks++;
|
|
+}
|
|
+
|
|
+
|
|
+#if 0
|
|
+static int yaffs_RewriteBufferedBlock(yaffs_Device *dev)
|
|
+{
|
|
+ dev->doingBufferedBlockRewrite = 1;
|
|
+ //
|
|
+ // Remove erased chunks
|
|
+ // Rewrite existing chunks to a new block
|
|
+ // Set current write block to the new block
|
|
+
|
|
+ dev->doingBufferedBlockRewrite = 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+
|
|
+static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND)
|
|
+{
|
|
+ int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
|
|
+
|
|
+ // Mark the block for retirement
|
|
+ yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
|
|
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND));
|
|
+
|
|
+
|
|
+ //TODO
|
|
+ // Just do a garbage collection on the affected block then retire the block
|
|
+ // NB recursion
|
|
+}
|
|
+
|
|
+
|
|
+static void yaffs_CheckWrittenBlock(yaffs_Device *dev,int chunkInNAND)
|
|
+{
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_ExtendedTags *tags)
|
|
+{
|
|
+}
|
|
+
|
|
+static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_ExtendedTags *tags)
|
|
+{
|
|
+}
|
|
+
|
|
+static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)
|
|
+{
|
|
+ int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
|
|
+
|
|
+ // Mark the block for retirement
|
|
+ yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
|
|
+ // Delete the chunk
|
|
+ yaffs_DeleteChunk(dev,chunkInNAND,1,__LINE__);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+#if 0
|
|
+static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1,int dataSize)
|
|
+{
|
|
+
|
|
+
|
|
+ if( memcmp(d0,d1,dataSize) != 0 ||
|
|
+ s0->tagByte0 != s1->tagByte0 ||
|
|
+ s0->tagByte1 != s1->tagByte1 ||
|
|
+ s0->tagByte2 != s1->tagByte2 ||
|
|
+ s0->tagByte3 != s1->tagByte3 ||
|
|
+ s0->tagByte4 != s1->tagByte4 ||
|
|
+ s0->tagByte5 != s1->tagByte5 ||
|
|
+ s0->tagByte6 != s1->tagByte6 ||
|
|
+ s0->tagByte7 != s1->tagByte7 ||
|
|
+ s0->ecc1[0] != s1->ecc1[0] ||
|
|
+ s0->ecc1[1] != s1->ecc1[1] ||
|
|
+ s0->ecc1[2] != s1->ecc1[2] ||
|
|
+ s0->ecc2[0] != s1->ecc2[0] ||
|
|
+ s0->ecc2[1] != s1->ecc2[1] ||
|
|
+ s0->ecc2[2] != s1->ecc2[2] )
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+///////////////////////// 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 YCHAR *name)
|
|
+{
|
|
+ __u16 sum = 0;
|
|
+ __u16 i = 1;
|
|
+
|
|
+ YUCHAR *bname = (YUCHAR *)name;
|
|
+ if(bname)
|
|
+ {
|
|
+ while ((*bname) && (i <=YAFFS_MAX_NAME_LENGTH))
|
|
+ {
|
|
+
|
|
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
|
|
+ sum += yaffs_toupper(*bname) * i;
|
|
+#else
|
|
+ sum += (*bname) * i;
|
|
+#endif
|
|
+ i++;
|
|
+ bname++;
|
|
+ }
|
|
+ }
|
|
+ return sum;
|
|
+}
|
|
+
|
|
+static void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name)
|
|
+{
|
|
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
|
|
+ if(name && yaffs_strlen(name) <= YAFFS_SHORT_NAME_LENGTH)
|
|
+ {
|
|
+ yaffs_strcpy(obj->shortName,name);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ obj->shortName[0]=_Y('\0');
|
|
+ }
|
|
+#endif
|
|
+ obj->sum = yaffs_CalcNameSum(name);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
|
|
+{
|
|
+ yaffs_ECCCalculate(data , spare->ecc1);
|
|
+ yaffs_ECCCalculate(&data[256] , spare->ecc2);
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+///////////////////////// 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)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not allocate Tnodes"TENDSTR)));
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ // Hook them into the free list
|
|
+ for(i = 0; i < nTnodes - 1; i++)
|
|
+ {
|
|
+ newTnodes[i].internal[0] = &newTnodes[i+1];
|
|
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
|
|
+ newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
|
|
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
|
|
+ newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
|
|
+#endif
|
|
+ dev->freeTnodes = newTnodes;
|
|
+ dev->nFreeTnodes+= nTnodes;
|
|
+ dev->nTnodesCreated += nTnodes;
|
|
+
|
|
+ // Now add this bunch of tnodes to a list for freeing up.
|
|
+ // NB If we can't add this to the management list it isn't fatal
|
|
+ // but it just means we can't free this bunch of tnodes later.
|
|
+ tnl = YMALLOC(sizeof(yaffs_TnodeList));
|
|
+ if(!tnl)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not add tnodes to management list" TENDSTR)));
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ tnl->tnodes = newTnodes;
|
|
+ tnl->next = dev->allocatedTnodeList;
|
|
+ dev->allocatedTnodeList = tnl;
|
|
+ }
|
|
+
|
|
+
|
|
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Tnodes added" TENDSTR)));
|
|
+
|
|
+
|
|
+ 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;
|
|
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
|
|
+ if(tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1)
|
|
+ {
|
|
+ // Hoosterman, this thing looks like it isn't in the list
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 1" TENDSTR)));
|
|
+ }
|
|
+#endif
|
|
+ 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)
|
|
+{
|
|
+ if(tn)
|
|
+ {
|
|
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
|
|
+ if(tn->internal[YAFFS_NTNODES_INTERNAL] != 0)
|
|
+ {
|
|
+ // Hoosterman, this thing looks like it is already in the list
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 2" TENDSTR)));
|
|
+ }
|
|
+ tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
|
|
+#endif
|
|
+ tn->internal[0] = dev->freeTnodes;
|
|
+ dev->freeTnodes = tn;
|
|
+ dev->nFreeTnodes++;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static void yaffs_DeinitialiseTnodes(yaffs_Device*dev)
|
|
+{
|
|
+ // Free the list of allocated tnodes
|
|
+ yaffs_TnodeList *tmp;
|
|
+
|
|
+ while(dev->allocatedTnodeList)
|
|
+ {
|
|
+ tmp = dev->allocatedTnodeList->next;
|
|
+
|
|
+ YFREE(dev->allocatedTnodeList->tnodes);
|
|
+ YFREE(dev->allocatedTnodeList);
|
|
+ dev->allocatedTnodeList = tmp;
|
|
+
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+
|
|
+}
|
|
+
|
|
+#if 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;
|
|
+ }
|
|
+
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+////////////////// 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;
|
|
+ int i;
|
|
+ int l;
|
|
+
|
|
+ __u32 x;
|
|
+
|
|
+
|
|
+ //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)
|
|
+
|
|
+ x = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);
|
|
+ requiredTallness = 0;
|
|
+ while(x)
|
|
+ {
|
|
+ x >>= 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
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("yaffs: no more tnodes" TENDSTR)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fStruct->topLevel = requiredTallness;
|
|
+ }
|
|
+
|
|
+
|
|
+ // Traverse down to level 0, adding anything we need
|
|
+
|
|
+ l = fStruct->topLevel;
|
|
+ tn = fStruct->top;
|
|
+ while (l > 0 && tn)
|
|
+ {
|
|
+ x = (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[x])
|
|
+ {
|
|
+ //T((TSTR(" added")));
|
|
+
|
|
+ tn->internal[x] = yaffs_GetTnode(dev);
|
|
+ }
|
|
+
|
|
+ tn = tn->internal[x];
|
|
+ l--;
|
|
+
|
|
+ }
|
|
+
|
|
+ //TSTR(TENDSTR)));
|
|
+
|
|
+ return tn;
|
|
+}
|
|
+
|
|
+
|
|
+static int yaffs_FindChunkInGroup(yaffs_Device *dev, int theChunk, yaffs_ExtendedTags *tags, int objectId, int chunkInInode)
|
|
+{
|
|
+ int j;
|
|
+
|
|
+
|
|
+ for(j = 0; theChunk && j < dev->chunkGroupSize; j++)
|
|
+ {
|
|
+ if(yaffs_CheckChunkBit(dev,theChunk / dev->nChunksPerBlock,theChunk % dev->nChunksPerBlock))
|
|
+ {
|
|
+ yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL,tags);
|
|
+ if(yaffs_TagsMatch(tags,objectId,chunkInInode))
|
|
+ {
|
|
+ // found it;
|
|
+ return theChunk;
|
|
+
|
|
+ }
|
|
+ }
|
|
+ theChunk++;
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+// DeleteWorker scans backwards through the tnode tree and deletes all the
|
|
+// chunks and tnodes in the file
|
|
+// Returns 1 if the tree was deleted. Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.
|
|
+
|
|
+static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit)
|
|
+{
|
|
+ int i;
|
|
+ int chunkInInode;
|
|
+ int theChunk;
|
|
+ yaffs_ExtendedTags tags;
|
|
+ int foundChunk;
|
|
+ yaffs_Device *dev = in->myDev;
|
|
+
|
|
+ int allDone = 1;
|
|
+
|
|
+
|
|
+ if(tn)
|
|
+ {
|
|
+ if(level > 0)
|
|
+ {
|
|
+
|
|
+ for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)
|
|
+ {
|
|
+ if(tn->internal[i])
|
|
+ {
|
|
+ if(limit && (*limit) < 0)
|
|
+ {
|
|
+ allDone = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ allDone = yaffs_DeleteWorker(in,tn->internal[i],level - 1,
|
|
+ (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i ,limit);
|
|
+ }
|
|
+ if(allDone)
|
|
+ {
|
|
+ yaffs_FreeTnode(dev,tn->internal[i]);
|
|
+ tn->internal[i] = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+ return (allDone) ? 1 : 0;
|
|
+ }
|
|
+ else if(level == 0)
|
|
+ {
|
|
+ int hitLimit = 0;
|
|
+
|
|
+ for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0 && !hitLimit; i--)
|
|
+ {
|
|
+ if(tn->level0[i])
|
|
+ {
|
|
+
|
|
+ chunkInInode = (chunkOffset << YAFFS_TNODES_LEVEL0_BITS ) + i;
|
|
+
|
|
+ theChunk = tn->level0[i] << dev->chunkGroupBits;
|
|
+
|
|
+ foundChunk = yaffs_FindChunkInGroup(dev,theChunk,&tags,in->objectId,chunkInInode);
|
|
+
|
|
+ if(foundChunk > 0)
|
|
+ {
|
|
+ yaffs_DeleteChunk(dev,foundChunk,1,__LINE__);
|
|
+ in->nDataChunks--;
|
|
+ if(limit)
|
|
+ {
|
|
+ *limit = *limit-1;
|
|
+ if(*limit <= 0)
|
|
+ {
|
|
+ hitLimit = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ tn->level0[i] = 0;
|
|
+ }
|
|
+
|
|
+ }
|
|
+ return (i < 0) ? 1 : 0;
|
|
+
|
|
+
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+static void yaffs_SoftDeleteChunk(yaffs_Device *dev, int chunk)
|
|
+{
|
|
+
|
|
+ yaffs_BlockInfo *theBlock;
|
|
+
|
|
+ T(YAFFS_TRACE_DELETION,(TSTR("soft delete chunk %d" TENDSTR),chunk));
|
|
+
|
|
+ theBlock = yaffs_GetBlockInfo(dev, chunk/dev->nChunksPerBlock);
|
|
+ if(theBlock)
|
|
+ {
|
|
+ theBlock->softDeletions++;
|
|
+ dev->nFreeChunks++;
|
|
+ }
|
|
+}
|
|
+
|
|
+// SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file.
|
|
+// All soft deleting does is increment the block's softdelete count and pulls the chunk out
|
|
+// of the tnode.
|
|
+// THus, essentially this is the same as DeleteWorker except that the chunks are soft deleted.
|
|
+//
|
|
+static int yaffs_SoftDeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset)
|
|
+{
|
|
+ int i;
|
|
+ int theChunk;
|
|
+ int allDone = 1;
|
|
+ yaffs_Device *dev = in->myDev;
|
|
+
|
|
+
|
|
+ if(tn)
|
|
+ {
|
|
+ if(level > 0)
|
|
+ {
|
|
+
|
|
+ for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)
|
|
+ {
|
|
+ if(tn->internal[i])
|
|
+ {
|
|
+ allDone = yaffs_SoftDeleteWorker(in,tn->internal[i],level - 1,
|
|
+ (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i);
|
|
+ if(allDone)
|
|
+ {
|
|
+ yaffs_FreeTnode(dev,tn->internal[i]);
|
|
+ tn->internal[i] = NULL;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ //Hoosterman... how could this happen.
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return (allDone) ? 1 : 0;
|
|
+ }
|
|
+ else if(level == 0)
|
|
+ {
|
|
+
|
|
+ for(i = YAFFS_NTNODES_LEVEL0 -1; i >=0; i--)
|
|
+ {
|
|
+ if(tn->level0[i])
|
|
+ {
|
|
+ // Note this does not find the real chunk, only the chunk group.
|
|
+ // We make an assumption that a chunk group is niot larger than a block.
|
|
+ theChunk = (tn->level0[i] << dev->chunkGroupBits);
|
|
+
|
|
+ yaffs_SoftDeleteChunk(dev,theChunk);
|
|
+ tn->level0[i] = 0;
|
|
+ }
|
|
+
|
|
+ }
|
|
+ return 1;
|
|
+
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static void yaffs_SoftDeleteFile(yaffs_Object *obj)
|
|
+{
|
|
+ if(obj->deleted &&
|
|
+ obj->variantType == YAFFS_OBJECT_TYPE_FILE &&
|
|
+ !obj->softDeleted)
|
|
+ {
|
|
+ if(obj->nDataChunks <= 0)
|
|
+ {
|
|
+ // Empty file with no duplicate object headers, just delete it immediately
|
|
+ yaffs_FreeTnode(obj->myDev,obj->variant.fileVariant.top);
|
|
+ obj->variant.fileVariant.top = NULL;
|
|
+ T(YAFFS_TRACE_TRACING,(TSTR("yaffs: Deleting empty file %d" TENDSTR),obj->objectId));
|
|
+ yaffs_DoGenericObjectDeletion(obj);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ yaffs_SoftDeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0);
|
|
+ obj->softDeleted = 1;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+// 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 <YAFFS_NTNODES_INTERNAL; i++)
|
|
+ {
|
|
+ if(tn->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)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Could not allocate more objects" TENDSTR)));
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ // Hook them into the free list
|
|
+ for(i = 0; i < nObjects - 1; i++)
|
|
+ {
|
|
+ newObjects[i].siblings.next = (struct list_head *)(&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)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("Could not add objects to management list" TENDSTR)));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ list->objects = newObjects;
|
|
+ list->next = dev->allocatedObjectList;
|
|
+ dev->allocatedObjectList = list;
|
|
+ }
|
|
+
|
|
+
|
|
+
|
|
+ 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->myDev = dev;
|
|
+ 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->deleted = 0;
|
|
+ obj->unlinked = 0;
|
|
+ obj->yst_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;
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+ if(tn->myInode)
|
|
+ {
|
|
+ // We're still hooked up to a cached inode.
|
|
+ // Don't delete now, but mark for later deletion
|
|
+ tn->deferedFree = 1;
|
|
+ return;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ yaffs_UnhashObject(tn);
|
|
+
|
|
+ // Link into the free list.
|
|
+ tn->siblings.next = (struct list_head *)(dev->freeObjects);
|
|
+ dev->freeObjects = tn;
|
|
+ dev->nFreeObjects++;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+
|
|
+void yaffs_HandleDeferedFree(yaffs_Object *obj)
|
|
+{
|
|
+ if(obj->deferedFree)
|
|
+ {
|
|
+ yaffs_FreeObject(obj);
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
+static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
|
|
+{
|
|
+ // Free the list of allocated Objects
|
|
+
|
|
+ yaffs_ObjectList *tmp;
|
|
+
|
|
+ while( dev->allocatedObjectList)
|
|
+ {
|
|
+ tmp = dev->allocatedObjectList->next;
|
|
+ YFREE(dev->allocatedObjectList->objects);
|
|
+ YFREE(dev->allocatedObjectList);
|
|
+
|
|
+ dev->allocatedObjectList = tmp;
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+static 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;
|
|
+
|
|
+ __u32 n = (__u32)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(i && 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;
|
|
+}
|
|
+
|
|
+static 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,__u32 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
|
|
+ if(i)
|
|
+ {
|
|
+ in = list_entry(i, yaffs_Object,hashLink);
|
|
+ if(in->objectId == number)
|
|
+ {
|
|
+#ifdef __KERNEL__
|
|
+ // Don't tell the VFS about this one if it is defered free
|
|
+ if(in->deferedFree)
|
|
+ return NULL;
|
|
+#endif
|
|
+
|
|
+ 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;
|
|
+ yaffs_HashObject(theObject);
|
|
+ theObject->variantType = type;
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_WinFileTimeNow(theObject->win_atime);
|
|
+ theObject->win_ctime[0] = theObject->win_mtime[0] = theObject->win_atime[0];
|
|
+ theObject->win_ctime[1] = theObject->win_mtime[1] = theObject->win_atime[1];
|
|
+
|
|
+#else
|
|
+
|
|
+ theObject->yst_atime = theObject->yst_mtime = theObject->yst_ctime = Y_CURRENT_TIME;
|
|
+#endif
|
|
+ switch(type)
|
|
+ {
|
|
+ case YAFFS_OBJECT_TYPE_FILE:
|
|
+ theObject->variant.fileVariant.fileSize = 0;
|
|
+ theObject->variant.fileVariant.scannedFileSize = 0;
|
|
+ theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF; // max __u32
|
|
+ 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;
|
|
+}
|
|
+
|
|
+static 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;
|
|
+
|
|
+}
|
|
+
|
|
+static YCHAR *yaffs_CloneString(const YCHAR *str)
|
|
+{
|
|
+ YCHAR *newStr = NULL;
|
|
+
|
|
+ if(str && *str)
|
|
+ {
|
|
+ newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR));
|
|
+ yaffs_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)
|
|
+static yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,
|
|
+ yaffs_Object *parent,
|
|
+ const YCHAR *name,
|
|
+ __u32 mode,
|
|
+ __u32 uid,
|
|
+ __u32 gid,
|
|
+ yaffs_Object *equivalentObject,
|
|
+ const YCHAR *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->yst_mode = mode;
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_WinFileTimeNow(in->win_atime);
|
|
+ in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];
|
|
+ in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
|
|
+
|
|
+#else
|
|
+ in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME;
|
|
+
|
|
+ in->yst_rdev = rdev;
|
|
+ in->yst_uid = uid;
|
|
+ in->yst_gid = gid;
|
|
+#endif
|
|
+ in->nDataChunks = 0;
|
|
+
|
|
+ yaffs_SetObjectName(in,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;
|
|
+ }
|
|
+
|
|
+ if(/*yaffs_GetNumberOfFreeChunks(dev) <= 0 || */
|
|
+ yaffs_UpdateObjectHeader(in,name,0,0,0) < 0)
|
|
+ {
|
|
+ // Could not create the object header, fail the creation
|
|
+ yaffs_DestroyObject(in);
|
|
+ in = NULL;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ return in;
|
|
+}
|
|
+
|
|
+yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const YCHAR *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 YCHAR *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 YCHAR *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev)
|
|
+{
|
|
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL,parent,name,mode,uid,gid,NULL,NULL,rdev);
|
|
+}
|
|
+
|
|
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const YCHAR *name, __u32 mode, __u32 uid, __u32 gid,const YCHAR *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 YCHAR *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 YCHAR *newName,int force,int shadows)
|
|
+{
|
|
+ int unlinkOp;
|
|
+ int deleteOp;
|
|
+
|
|
+ yaffs_Object * existingTarget;
|
|
+
|
|
+ if(newDir == NULL)
|
|
+ {
|
|
+ newDir = obj->parent; // use the old directory
|
|
+ }
|
|
+
|
|
+ if(newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragendy: yaffs_ChangeObjectName: newDir is not a directory"TENDSTR)));
|
|
+ YBUG();
|
|
+ }
|
|
+
|
|
+ // TODO: Do we need this different handling for YAFFS2 and YAFFS1??
|
|
+ if(obj->myDev->isYaffs2)
|
|
+ {
|
|
+ unlinkOp = (newDir == obj->myDev->unlinkedDir);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ unlinkOp = (newDir == obj->myDev->unlinkedDir && obj->variantType == YAFFS_OBJECT_TYPE_FILE);
|
|
+ }
|
|
+
|
|
+ deleteOp = (newDir == obj->myDev->deletedDir);
|
|
+
|
|
+ existingTarget = yaffs_FindObjectByName(newDir,newName);
|
|
+
|
|
+ // If the object is a file going into the unlinked directory, then it is OK to just stuff it in since
|
|
+ // duplicate names are allowed.
|
|
+ // Otherwise only proceed if the new name does not exist and if we're putting it into a directory.
|
|
+ if( (unlinkOp||
|
|
+ deleteOp ||
|
|
+ force ||
|
|
+ (shadows > 0) ||
|
|
+ !existingTarget) &&
|
|
+ newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
|
|
+ {
|
|
+ yaffs_SetObjectName(obj,newName);
|
|
+ obj->dirty = 1;
|
|
+
|
|
+ yaffs_AddObjectToDirectory(newDir,obj);
|
|
+
|
|
+ if(unlinkOp) obj->unlinked = 1;
|
|
+
|
|
+ // If it is a deletion then we mark it as a shrink for gc purposes.
|
|
+ if(yaffs_UpdateObjectHeader(obj,newName,0,deleteOp,shadows) >= 0)
|
|
+ {
|
|
+ return YAFFS_OK;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return YAFFS_FAIL;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName, yaffs_Object *newDir, const YCHAR *newName)
|
|
+{
|
|
+ yaffs_Object *obj;
|
|
+ yaffs_Object *existingTarget;
|
|
+ int force = 0;
|
|
+
|
|
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
|
|
+ // Special case for case insemsitive systems (eg. WinCE).
|
|
+ // While look-up is case insensitive, the name isn't.
|
|
+ // THerefore we might want to change x.txt to X.txt
|
|
+ if(oldDir == newDir && yaffs_strcmp(oldName,newName) == 0)
|
|
+ {
|
|
+ force = 1;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ obj = yaffs_FindObjectByName(oldDir,oldName);
|
|
+
|
|
+ if(obj && obj->renameAllowed)
|
|
+ {
|
|
+
|
|
+ // Now do the handling for an existing target, if there is one
|
|
+
|
|
+ existingTarget = yaffs_FindObjectByName(newDir,newName);
|
|
+ if(existingTarget &&
|
|
+ existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
|
|
+ !list_empty(&existingTarget->variant.directoryVariant.children))
|
|
+ {
|
|
+ // There is a target that is a non-empty directory, so we have to fail
|
|
+ return YAFFS_FAIL; // EEXIST or ENOTEMPTY
|
|
+ }
|
|
+ else if(existingTarget)
|
|
+ {
|
|
+ // Nuke the target first, using shadowing
|
|
+ yaffs_ChangeObjectName(obj,newDir,newName,force,existingTarget->objectId);
|
|
+ yaffs_Unlink(newDir,newName);
|
|
+ }
|
|
+
|
|
+
|
|
+ return yaffs_ChangeObjectName(obj,newDir,newName,force,0);
|
|
+ }
|
|
+ return YAFFS_FAIL;
|
|
+}
|
|
+
|
|
+
|
|
+#if 0
|
|
+
|
|
+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)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("Inode hash inconsistency" TENDSTR)));
|
|
+ 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);
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+/////////////////////////// Block Management and Page Allocation ///////////////////
|
|
+
|
|
+
|
|
+static int yaffs_InitialiseBlocks(yaffs_Device *dev,int nBlocks)
|
|
+{
|
|
+ dev->allocationBlock = -1; // force it to get a new one
|
|
+ //Todo we're assuming the malloc will pass.
|
|
+ dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));
|
|
+ // Set up dynamic blockinfo stuff.
|
|
+ dev->chunkBitmapStride = (dev->nChunksPerBlock+7)/8;
|
|
+ dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
|
|
+ if(dev->blockInfo && dev->chunkBits)
|
|
+ {
|
|
+ memset(dev->blockInfo,0,nBlocks * sizeof(yaffs_BlockInfo));
|
|
+ memset(dev->chunkBits,0,dev->chunkBitmapStride * nBlocks);
|
|
+ return YAFFS_OK;
|
|
+ }
|
|
+
|
|
+ return YAFFS_FAIL;
|
|
+
|
|
+}
|
|
+
|
|
+static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
|
|
+{
|
|
+ YFREE(dev->blockInfo);
|
|
+ dev->blockInfo = NULL;
|
|
+ YFREE(dev->chunkBits);
|
|
+ dev->chunkBits = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev, yaffs_BlockInfo *bi)
|
|
+{
|
|
+ int i;
|
|
+ __u32 seq;
|
|
+ yaffs_BlockInfo *b;
|
|
+
|
|
+ if(!dev->isYaffs2) return 1; // disqualification only applies to yaffs2.
|
|
+
|
|
+ if(!bi->hasShrinkHeader) return 1; // can gc
|
|
+
|
|
+
|
|
+ // Find the oldest dirty sequence number if we don't know it and save it
|
|
+ // so we don't have to keep recomputing it.
|
|
+ if(!dev->oldestDirtySequence)
|
|
+ {
|
|
+ seq = dev->sequenceNumber;
|
|
+
|
|
+ for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++)
|
|
+ {
|
|
+ b = yaffs_GetBlockInfo(dev,i);
|
|
+ if(b->blockState == YAFFS_BLOCK_STATE_FULL &&
|
|
+ (b->pagesInUse - b->softDeletions )< dev->nChunksPerBlock &&
|
|
+ b->sequenceNumber < seq)
|
|
+ {
|
|
+ seq = b->sequenceNumber;
|
|
+ }
|
|
+ }
|
|
+ dev->oldestDirtySequence = seq;
|
|
+ }
|
|
+
|
|
+
|
|
+ // Can't do gc of this block if there are any blocks older than this one that have
|
|
+ // discarded pages.
|
|
+ return (bi->sequenceNumber <= dev->oldestDirtySequence);
|
|
+
|
|
+
|
|
+ return 1;
|
|
+
|
|
+}
|
|
+
|
|
+// FindDiretiestBlock is used to select the dirtiest block (or close enough)
|
|
+// for garbage collection.
|
|
+//
|
|
+
|
|
+
|
|
+
|
|
+static int yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,int aggressive)
|
|
+{
|
|
+
|
|
+ int b = dev->currentDirtyChecker;
|
|
+
|
|
+ int i;
|
|
+ int iterations;
|
|
+ int dirtiest = -1;
|
|
+ int pagesInUse;
|
|
+ yaffs_BlockInfo *bi;
|
|
+ static int nonAggressiveSkip = 0;
|
|
+
|
|
+ // If we're doing aggressive GC then we are happy to take a less-dirty block, and
|
|
+ // search harder.
|
|
+ // else (we're doing a leasurely gc), then we only bother to do this if the
|
|
+ // block has only a few pages in use.
|
|
+
|
|
+
|
|
+ nonAggressiveSkip--;
|
|
+
|
|
+ if(!aggressive &&(nonAggressiveSkip > 0))
|
|
+ {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ pagesInUse = (aggressive)? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
|
|
+
|
|
+ if(aggressive)
|
|
+ {
|
|
+ iterations = dev->internalEndBlock - dev->internalStartBlock + 1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ iterations = dev->internalEndBlock - dev->internalStartBlock + 1;
|
|
+ iterations = iterations / 16;
|
|
+ if(iterations > 200)
|
|
+ {
|
|
+ iterations = 200;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for(i = 0; i <= iterations && pagesInUse > 0 ; i++)
|
|
+ {
|
|
+ b++;
|
|
+ if ( b < dev->internalStartBlock || b > dev->internalEndBlock)
|
|
+ {
|
|
+ b = dev->internalStartBlock;
|
|
+ }
|
|
+
|
|
+ if(b < dev->internalStartBlock || b > dev->internalEndBlock)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> Block %d is not valid" TENDSTR),b));
|
|
+ YBUG();
|
|
+ }
|
|
+
|
|
+ bi = yaffs_GetBlockInfo(dev,b);
|
|
+
|
|
+ if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&
|
|
+ (bi->pagesInUse - bi->softDeletions )< pagesInUse &&
|
|
+ yaffs_BlockNotDisqualifiedFromGC(dev,bi))
|
|
+ {
|
|
+ dirtiest = b;
|
|
+ pagesInUse = (bi->pagesInUse - bi->softDeletions);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dev->currentDirtyChecker = b;
|
|
+
|
|
+ if(dirtiest > 0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_GC,(TSTR("GC Selected block %d with %d free" TENDSTR),dirtiest,dev->nChunksPerBlock - pagesInUse));
|
|
+ }
|
|
+
|
|
+ dev->oldestDirtySequence = 0; // clear this
|
|
+
|
|
+ if(dirtiest > 0)
|
|
+ {
|
|
+ nonAggressiveSkip = 4;
|
|
+ }
|
|
+
|
|
+ return dirtiest;
|
|
+}
|
|
+
|
|
+
|
|
+static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)
|
|
+{
|
|
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,blockNo);
|
|
+
|
|
+ int erasedOk = 0;
|
|
+
|
|
+ // If the block is still healthy erase it and mark as clean.
|
|
+ // If the block has had a data failure, then retire it.
|
|
+ bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
|
|
+
|
|
+ if(!bi->needsRetiring)
|
|
+ {
|
|
+ erasedOk = yaffs_EraseBlockInNAND(dev,blockNo);
|
|
+ if(!erasedOk)
|
|
+ {
|
|
+ dev->nErasureFailures++;
|
|
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Erasure failed %d" TENDSTR),blockNo));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(erasedOk && (yaffs_traceMask & YAFFS_TRACE_ERASE))
|
|
+ {
|
|
+ int i;
|
|
+ for(i = 0; i < dev->nChunksPerBlock; i++)
|
|
+ {
|
|
+ if(!yaffs_CheckChunkErased(dev,blockNo * dev->nChunksPerBlock + i))
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR(">>Block %d erasure supposedly OK, but chunk %d not erased" TENDSTR),blockNo,i));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if( erasedOk )
|
|
+ {
|
|
+ // Clean it up...
|
|
+ bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
|
|
+ dev->nErasedBlocks++;
|
|
+ bi->pagesInUse = 0;
|
|
+ bi->softDeletions = 0;
|
|
+ bi->hasShrinkHeader=0;
|
|
+ yaffs_ClearChunkBits(dev,blockNo);
|
|
+
|
|
+ T(YAFFS_TRACE_ERASE,(TSTR("Erased block %d" TENDSTR),blockNo));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ dev->nFreeChunks -= dev->nChunksPerBlock; // We lost a block of free space
|
|
+
|
|
+ yaffs_RetireBlock(dev,blockNo);
|
|
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Block %d retired" TENDSTR),blockNo));
|
|
+ }
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static void yaffs_DumpBlockStats(yaffs_Device *dev)
|
|
+{
|
|
+ int i,j;
|
|
+ yaffs_BlockInfo *bi;
|
|
+
|
|
+ for(i= dev->internalStartBlock; i <=dev->internalEndBlock; i++)
|
|
+ {
|
|
+ bi = yaffs_GetBlockInfo(dev,i);
|
|
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("%3d state %d shrink %d inuse %d/%d seq %d pages"),i,
|
|
+ bi->blockState,bi->hasShrinkHeader,bi->pagesInUse,bi->softDeletions,bi->sequenceNumber));
|
|
+
|
|
+ for(j = 0; j < dev->nChunksPerBlock; j++)
|
|
+ {
|
|
+ if(yaffs_CheckChunkBit(dev,i,j))
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALLOCATE,(TSTR(" %d"),j));
|
|
+
|
|
+ }
|
|
+ }
|
|
+ T(YAFFS_TRACE_ALLOCATE,(TSTR(" " TENDSTR)));
|
|
+
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ yaffs_BlockInfo *bi;
|
|
+
|
|
+#if 0
|
|
+ static int j = 0;
|
|
+ j++;
|
|
+ if(j < 0 || j > 100)
|
|
+ {
|
|
+ j = 0;
|
|
+ yaffs_DumpBlockStats(dev);
|
|
+ }
|
|
+
|
|
+#endif
|
|
+
|
|
+ if(dev->nErasedBlocks < 1)
|
|
+ {
|
|
+ // Hoosterman we've got a problem.
|
|
+ // Can't get space to gc
|
|
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no more eraased blocks" TENDSTR)));
|
|
+
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ // Find an empty block.
|
|
+
|
|
+ for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++)
|
|
+ {
|
|
+ dev->allocationBlockFinder++;
|
|
+ if(dev->allocationBlockFinder < dev->internalStartBlock || dev->allocationBlockFinder> dev->internalEndBlock)
|
|
+ {
|
|
+ dev->allocationBlockFinder = dev->internalStartBlock;
|
|
+ }
|
|
+
|
|
+ bi = yaffs_GetBlockInfo(dev,dev->allocationBlockFinder);
|
|
+
|
|
+ if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
|
|
+ {
|
|
+ bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
|
|
+ dev->sequenceNumber++;
|
|
+ bi->sequenceNumber = dev->sequenceNumber;
|
|
+ dev->nErasedBlocks--;
|
|
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("Allocated block %d, seq %d, %d left" TENDSTR),dev->allocationBlockFinder,dev->sequenceNumber, dev->nErasedBlocks));
|
|
+ return dev->allocationBlockFinder;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs tragedy: no more eraased blocks, but there should have been %d" TENDSTR),dev->nErasedBlocks));
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+
|
|
+// To determine if we have enough space we just look at the
|
|
+// number of erased blocks.
|
|
+
|
|
+static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev)
|
|
+{
|
|
+ int reservedChunks = (dev->nReservedBlocks * dev->nChunksPerBlock);
|
|
+ return (dev->nFreeChunks > reservedChunks);
|
|
+}
|
|
+
|
|
+
|
|
+static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
|
|
+{
|
|
+ int retVal;
|
|
+ yaffs_BlockInfo *bi;
|
|
+
|
|
+ if(dev->allocationBlock < 0)
|
|
+ {
|
|
+ // Get next block to allocate off
|
|
+ dev->allocationBlock = yaffs_FindBlockForAllocation(dev);
|
|
+ dev->allocationPage = 0;
|
|
+ }
|
|
+
|
|
+ if(!useReserve && !yaffs_CheckSpaceForAllocation(dev))
|
|
+ {
|
|
+ // Not enough space to allocate unless we're allowed to use the reserve.
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if(dev->nErasedBlocks < dev->nReservedBlocks && dev->allocationPage == 0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("Allocating reserve" TENDSTR)));
|
|
+ }
|
|
+
|
|
+
|
|
+ // Next page please....
|
|
+ if(dev->allocationBlock >= 0)
|
|
+ {
|
|
+ bi = yaffs_GetBlockInfo(dev,dev->allocationBlock);
|
|
+
|
|
+ retVal = (dev->allocationBlock * dev->nChunksPerBlock) +
|
|
+ dev->allocationPage;
|
|
+ bi->pagesInUse++;
|
|
+ yaffs_SetChunkBit(dev,dev->allocationBlock,dev->allocationPage);
|
|
+
|
|
+ dev->allocationPage++;
|
|
+
|
|
+ dev->nFreeChunks--;
|
|
+
|
|
+ // If the block is full set the state to full
|
|
+ if(dev->allocationPage >= dev->nChunksPerBlock)
|
|
+ {
|
|
+ bi->blockState = YAFFS_BLOCK_STATE_FULL;
|
|
+ dev->allocationBlock = -1;
|
|
+ }
|
|
+
|
|
+
|
|
+ return retVal;
|
|
+
|
|
+ }
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+static int yaffs_GetErasedChunks(yaffs_Device *dev)
|
|
+{
|
|
+ int n;
|
|
+
|
|
+ n = dev->nErasedBlocks * dev->nChunksPerBlock;
|
|
+
|
|
+ if(dev->allocationBlock> 0)
|
|
+ {
|
|
+ n += (dev->nChunksPerBlock - dev->allocationPage);
|
|
+ }
|
|
+
|
|
+ return n;
|
|
+
|
|
+}
|
|
+
|
|
+static int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
|
|
+{
|
|
+ int oldChunk;
|
|
+ int newChunk;
|
|
+ int chunkInBlock;
|
|
+ int markNAND;
|
|
+ int retVal = YAFFS_OK;
|
|
+ int cleanups = 0;
|
|
+ int i;
|
|
+
|
|
+ int chunksBefore = yaffs_GetErasedChunks(dev);
|
|
+ int chunksAfter;
|
|
+
|
|
+ yaffs_ExtendedTags tags;
|
|
+
|
|
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);
|
|
+
|
|
+ yaffs_Object *object;
|
|
+
|
|
+ bi->blockState = YAFFS_BLOCK_STATE_COLLECTING;
|
|
+
|
|
+ T(YAFFS_TRACE_TRACING,(TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR),block,bi->pagesInUse,bi->hasShrinkHeader));
|
|
+ //T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));
|
|
+
|
|
+ //yaffs_VerifyFreeChunks(dev);
|
|
+
|
|
+ bi->hasShrinkHeader = 0; // clear the flag so that the block can erase
|
|
+
|
|
+ dev->nFreeChunks -= bi->softDeletions; // Take off the number of soft deleted entries because
|
|
+ // they're going to get really deleted during GC.
|
|
+
|
|
+ dev->isDoingGC = 1;
|
|
+
|
|
+ if(!yaffs_StillSomeChunkBits(dev,block))
|
|
+ {
|
|
+ T(YAFFS_TRACE_TRACING,(TSTR("Collecting block %d that has no chunks in use" TENDSTR),block));
|
|
+ yaffs_BlockBecameDirty(dev,block);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+
|
|
+ __u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__);
|
|
+
|
|
+ for(chunkInBlock = 0,oldChunk = block * dev->nChunksPerBlock;
|
|
+ chunkInBlock < dev->nChunksPerBlock && yaffs_StillSomeChunkBits(dev,block);
|
|
+ chunkInBlock++, oldChunk++ )
|
|
+ {
|
|
+ if(yaffs_CheckChunkBit(dev,block,chunkInBlock))
|
|
+ {
|
|
+
|
|
+ // This page is in use and might need to be copied off
|
|
+
|
|
+ markNAND = 1;
|
|
+
|
|
+ //T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));
|
|
+
|
|
+ yaffs_InitialiseTags(&tags);
|
|
+
|
|
+ yaffs_ReadChunkWithTagsFromNAND(dev,oldChunk,buffer, &tags);
|
|
+
|
|
+ object = yaffs_FindObjectByNumber(dev,tags.objectId);
|
|
+
|
|
+ T(YAFFS_TRACE_GC_DETAIL,(TSTR("Collecting page %d, %d %d %d " TENDSTR),chunkInBlock,tags.objectId,tags.chunkId,tags.byteCount));
|
|
+
|
|
+ if(!object)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("page %d in gc has no object " TENDSTR),oldChunk));
|
|
+ }
|
|
+
|
|
+ if(object && object->deleted && tags.chunkId != 0)
|
|
+ {
|
|
+ // Data chunk in a deleted file, throw it away
|
|
+ // It's a soft deleted data chunk,
|
|
+ // No need to copy this, just forget about it and fix up the
|
|
+ // object.
|
|
+
|
|
+ //yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0);
|
|
+ object->nDataChunks--;
|
|
+
|
|
+ if(object->nDataChunks <= 0)
|
|
+ {
|
|
+ // remeber to clean up the object
|
|
+ dev->gcCleanupList[cleanups] = tags.objectId;
|
|
+ cleanups++;
|
|
+ }
|
|
+ markNAND = 0;
|
|
+ }
|
|
+ else if( 0 /* Todo object && object->deleted && object->nDataChunks == 0 */)
|
|
+ {
|
|
+ // Deleted object header with no data chunks.
|
|
+ // Can be discarded and the file deleted.
|
|
+ object->chunkId = 0;
|
|
+ yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
|
|
+ object->variant.fileVariant.top = NULL;
|
|
+ yaffs_DoGenericObjectDeletion(object);
|
|
+
|
|
+ }
|
|
+ else if(object)
|
|
+ {
|
|
+ // It's either a data chunk in a live file or
|
|
+ // an ObjectHeader, so we're interested in it.
|
|
+ // NB Need to keep the ObjectHeaders of deleted files
|
|
+ // until the whole file has been deleted off
|
|
+ tags.serialNumber++;
|
|
+
|
|
+ dev->nGCCopies++;
|
|
+
|
|
+ if(tags.chunkId == 0)
|
|
+ {
|
|
+ // It is an object Id,
|
|
+ // We need to nuke the shrinkheader flags first
|
|
+ // We no longer want the shrinkHeader flag since its work is done
|
|
+ // and if it is left in place it will mess up scanning.
|
|
+
|
|
+ yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
|
|
+ oh->isShrink = 0;
|
|
+ tags.extraIsShrinkHeader = 0;
|
|
+ }
|
|
+
|
|
+ newChunk = yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags,1);
|
|
+
|
|
+ if(newChunk < 0)
|
|
+ {
|
|
+ retVal = YAFFS_FAIL;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+
|
|
+ // Ok, now fix up the Tnodes etc.
|
|
+
|
|
+ if(tags.chunkId == 0)
|
|
+ {
|
|
+ // It's a header
|
|
+ object->chunkId = newChunk;
|
|
+ object->serial = tags.serialNumber;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // It's a data chunk
|
|
+ yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ yaffs_DeleteChunk(dev,oldChunk,markNAND,__LINE__);
|
|
+
|
|
+ }
|
|
+ }
|
|
+
|
|
+ yaffs_ReleaseTempBuffer(dev,buffer,__LINE__);
|
|
+
|
|
+ //yaffs_VerifyFreeChunks(dev);
|
|
+
|
|
+ // Do any required cleanups
|
|
+ for(i = 0; i < cleanups; i++)
|
|
+ {
|
|
+ // Time to delete the file too
|
|
+ object = yaffs_FindObjectByNumber(dev,dev->gcCleanupList[i]);
|
|
+ if(object)
|
|
+ {
|
|
+ yaffs_FreeTnode(dev,object->variant.fileVariant.top);
|
|
+ object->variant.fileVariant.top = NULL;
|
|
+ T(YAFFS_TRACE_GC,(TSTR("yaffs: About to finally delete object %d" TENDSTR),object->objectId));
|
|
+ yaffs_DoGenericObjectDeletion(object);
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ if(chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev)))
|
|
+ {
|
|
+ T(YAFFS_TRACE_GC,(TSTR("gc did not increase free chunks before %d after %d" TENDSTR),chunksBefore,chunksAfter));
|
|
+ }
|
|
+
|
|
+
|
|
+ dev->isDoingGC = 0;
|
|
+
|
|
+ //yaffs_VerifyFreeChunks(dev);
|
|
+
|
|
+ return YAFFS_OK;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev)
|
|
+{
|
|
+ // find a file to delete
|
|
+ struct list_head *i;
|
|
+ yaffs_Object *l;
|
|
+
|
|
+
|
|
+ //Scan the unlinked files looking for one to delete
|
|
+ list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
|
|
+ {
|
|
+ if(i)
|
|
+ {
|
|
+ l = list_entry(i, yaffs_Object,siblings);
|
|
+ if(l->deleted)
|
|
+ {
|
|
+ return l;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+
|
|
+static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)
|
|
+{
|
|
+ // This does background deletion on unlinked files.. only deleted ones.
|
|
+ // If we don't have a file we're working on then find one
|
|
+ if(!dev->unlinkedDeletion && dev->nDeletedFiles > 0)
|
|
+ {
|
|
+ dev->unlinkedDeletion = yaffs_FindDeletedUnlinkedFile(dev);
|
|
+ }
|
|
+
|
|
+ // OK, we're working on a file...
|
|
+ if(dev->unlinkedDeletion)
|
|
+ {
|
|
+ yaffs_Object *obj = dev->unlinkedDeletion;
|
|
+ int delresult;
|
|
+ int limit; // Number of chunks to delete in a file.
|
|
+ // NB this can be exceeded, but not by much.
|
|
+
|
|
+ limit = -1;
|
|
+
|
|
+ delresult = yaffs_DeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0,&limit);
|
|
+
|
|
+ if(obj->nDataChunks == 0)
|
|
+ {
|
|
+ // Done all the deleting of data chunks.
|
|
+ // Now dump the header and clean up
|
|
+ yaffs_FreeTnode(dev,obj->variant.fileVariant.top);
|
|
+ obj->variant.fileVariant.top = NULL;
|
|
+ yaffs_DoGenericObjectDeletion(obj);
|
|
+ dev->nDeletedFiles--;
|
|
+ dev->nUnlinkedFiles--;
|
|
+ dev->nBackgroundDeletions++;
|
|
+ dev->unlinkedDeletion = NULL;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
+// New garbage collector
|
|
+// If we're very low on erased blocks then we do aggressive garbage collection
|
|
+// otherwise we do "leasurely" garbage collection.
|
|
+// Aggressive gc looks further (whole array) and will accept dirtier blocks.
|
|
+// Passive gc only inspects smaller areas and will only accept cleaner blocks.
|
|
+//
|
|
+// The idea is to help clear out space in a more spread-out manner.
|
|
+// Dunno if it really does anything useful.
|
|
+//
|
|
+static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
|
|
+{
|
|
+ int block;
|
|
+ int aggressive;
|
|
+ int gcOk = YAFFS_OK;
|
|
+ int maxTries = 0;
|
|
+
|
|
+ //yaffs_VerifyFreeChunks(dev);
|
|
+
|
|
+ if(dev->isDoingGC)
|
|
+ {
|
|
+ // Bail out so we don't get recursive gc
|
|
+ return YAFFS_OK;
|
|
+ }
|
|
+
|
|
+ // This loop should pass the first time.
|
|
+ // We'll only see looping here if the erase of the collected block fails.
|
|
+
|
|
+ do{
|
|
+ maxTries++;
|
|
+ if(dev->nErasedBlocks < dev->nReservedBlocks)
|
|
+ {
|
|
+ // We need a block soon...
|
|
+ aggressive = 1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // We're in no hurry
|
|
+ aggressive = 0;
|
|
+ }
|
|
+
|
|
+ block = yaffs_FindBlockForGarbageCollection(dev,aggressive);
|
|
+
|
|
+ if(block > 0)
|
|
+ {
|
|
+ dev->garbageCollections++;
|
|
+ if(!aggressive)
|
|
+ {
|
|
+ dev->passiveGarbageCollections++;
|
|
+ }
|
|
+
|
|
+ T(YAFFS_TRACE_GC,(TSTR("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),dev->nErasedBlocks,aggressive));
|
|
+
|
|
+ gcOk = yaffs_GarbageCollectBlock(dev,block);
|
|
+ }
|
|
+
|
|
+ if(dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_GC,(TSTR("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d" TENDSTR),dev->nErasedBlocks,maxTries,block));
|
|
+ }
|
|
+ } while((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0) && (maxTries < 2));
|
|
+
|
|
+ return aggressive ? gcOk: YAFFS_OK;
|
|
+}
|
|
+
|
|
+
|
|
+//////////////////////////// TAGS ///////////////////////////////////////
|
|
+
|
|
+
|
|
+
|
|
+#if 0
|
|
+
|
|
+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 &0xff; j<<=1)
|
|
+ {
|
|
+ bit++;
|
|
+ if(b[i] & j)
|
|
+ {
|
|
+ ecc ^= bit;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ tags->ecc = ecc;
|
|
+
|
|
+
|
|
+}
|
|
+
|
|
+int yaffs_CheckECCOnTags(yaffs_Tags *tags)
|
|
+{
|
|
+ unsigned ecc = tags->ecc;
|
|
+
|
|
+ yaffs_CalcTagsECC(tags);
|
|
+
|
|
+ ecc ^= tags->ecc;
|
|
+
|
|
+ if(ecc && ecc <= 64)
|
|
+ {
|
|
+ // TODO: Handle the failure better. Retire?
|
|
+ unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
|
|
+
|
|
+ ecc--;
|
|
+
|
|
+ b[ecc / 8] ^= (1 << (ecc & 7));
|
|
+
|
|
+ // Now recvalc the ecc
|
|
+ yaffs_CalcTagsECC(tags);
|
|
+
|
|
+ return 1; // recovered error
|
|
+ }
|
|
+ else if(ecc)
|
|
+ {
|
|
+ // Wierd ecc failure value
|
|
+ // TODO Need to do somethiong here
|
|
+ return -1; //unrecovered error
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+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_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)
|
|
+{
|
|
+ yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
|
|
+ int result;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ result = yaffs_CheckECCOnTags(tagsPtr);
|
|
+ if(result> 0)
|
|
+ {
|
|
+ dev->tagsEccFixed++;
|
|
+ }
|
|
+ else if(result <0)
|
|
+ {
|
|
+ dev->tagsEccUnfixed++;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void yaffs_SpareInitialise(yaffs_Spare *spare)
|
|
+{
|
|
+ memset(spare,0xFF,sizeof(yaffs_Spare));
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+#if 0
|
|
+static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_ExtendedTags *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(!dev->useNANDECC && buffer)
|
|
+ //{
|
|
+ // yaffs_CalcECC(buffer,&spare);
|
|
+ //}
|
|
+
|
|
+ //yaffs_LoadTagsIntoSpare(&spare,tags);
|
|
+
|
|
+ return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve);
|
|
+
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int yaffs_TagsMatch(const yaffs_ExtendedTags *tags, int objectId, int chunkInObject)
|
|
+{
|
|
+ return ( tags->chunkId == chunkInObject &&
|
|
+ tags->objectId == objectId &&
|
|
+ !tags->chunkDeleted) ? 1 : 0;
|
|
+
|
|
+}
|
|
+
|
|
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
+
|
|
+
|
|
+static int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_ExtendedTags *tags)
|
|
+{
|
|
+ //Get the Tnode, then get the level 0 offset chunk offset
|
|
+ yaffs_Tnode *tn;
|
|
+ int theChunk = -1;
|
|
+ yaffs_ExtendedTags localTags;
|
|
+ int retVal = -1;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ retVal = yaffs_FindChunkInGroup(dev,theChunk,tags,in->objectId,chunkInInode);
|
|
+ }
|
|
+ return retVal;
|
|
+}
|
|
+
|
|
+static int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_ExtendedTags *tags)
|
|
+{
|
|
+ //Get the Tnode, then get the level 0 offset chunk offset
|
|
+ yaffs_Tnode *tn;
|
|
+ int theChunk = -1;
|
|
+ yaffs_ExtendedTags localTags;
|
|
+
|
|
+ yaffs_Device *dev = in->myDev;
|
|
+ int retVal = -1;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ retVal = yaffs_FindChunkInGroup(dev,theChunk,tags,in->objectId,chunkInInode);
|
|
+
|
|
+ // Delete the entry in the filestructure (if found)
|
|
+ if(retVal != -1)
|
|
+ {
|
|
+ tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ //T(("No level 0 found for %d\n", chunkInInode));
|
|
+ }
|
|
+
|
|
+ if(retVal == -1)
|
|
+ {
|
|
+ //T(("Could not find %d to delete\n",chunkInInode));
|
|
+ }
|
|
+ return retVal;
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef 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 + in->myDev->nBytesPerChunk -1)/in->myDev->nBytesPerChunk;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ if(yaffs_CheckChunkBits(dev,theChunk/dev->nChunksPerBlock,theChunk%dev->nChunksPerBlock))
|
|
+ {
|
|
+
|
|
+
|
|
+ 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)
|
|
+{
|
|
+ // NB inScan is zero unless scanning. For forward scanning, inScan is > 0; for backward scanning inScan is < 0
|
|
+ yaffs_Tnode *tn;
|
|
+ yaffs_Device *dev = in->myDev;
|
|
+ int existingChunk;
|
|
+ yaffs_ExtendedTags existingTags;
|
|
+ yaffs_ExtendedTags newTags;
|
|
+ unsigned existingSerial, newSerial;
|
|
+
|
|
+ if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
|
|
+ {
|
|
+ // Just ignore an attempt at putting a chunk into a non-file during scanning
|
|
+ // If it is not during Scanning then something went wrong!
|
|
+ if(!inScan)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy:attempt to put data chunk into a non-file" TENDSTR)));
|
|
+ YBUG();
|
|
+ }
|
|
+
|
|
+ yaffs_DeleteChunk(dev,chunkInNAND,1,__LINE__);
|
|
+ return YAFFS_OK;
|
|
+ }
|
|
+
|
|
+ tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
|
|
+ if(!tn)
|
|
+ {
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK];
|
|
+
|
|
+ if(inScan != 0)
|
|
+ {
|
|
+ // 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.
|
|
+ //
|
|
+ // Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO
|
|
+ // Update: For backward scanning we don't need to re-read tags so this is quite cheap.
|
|
+
|
|
+
|
|
+
|
|
+ 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:
|
|
+ //
|
|
+ // Backwards scanning YAFFS2: The old one is what we use, dump the new one.
|
|
+ // Forward scanning YAFFS2: The new one is what we use, dump the old one.
|
|
+ // YAFFS1: Get both sets of tags and compare serial numbers.
|
|
+ //
|
|
+ //
|
|
+
|
|
+ if(inScan > 0)
|
|
+ {
|
|
+ // Only do this for forward scanning
|
|
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunkInNAND, NULL,&newTags);
|
|
+
|
|
+
|
|
+ // Do a proper find
|
|
+ existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags);
|
|
+ }
|
|
+
|
|
+ if(existingChunk <=0)
|
|
+ {
|
|
+ //Hoosterman - how did this happen?
|
|
+
|
|
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: existing chunk < 0 in scan" TENDSTR)));
|
|
+
|
|
+ }
|
|
+
|
|
+
|
|
+ // NB The deleted flags should be false, otherwise the chunks will
|
|
+ // not be loaded during a scan
|
|
+
|
|
+ newSerial = newTags.serialNumber;
|
|
+ existingSerial = existingTags.serialNumber;
|
|
+
|
|
+ if( (inScan > 0) &&
|
|
+ ( in->myDev->isYaffs2 ||
|
|
+ existingChunk <= 0 ||
|
|
+ ((existingSerial+1) & 3) == newSerial))
|
|
+ {
|
|
+ // Forward scanning.
|
|
+ // Use new
|
|
+ // Delete the old one and drop through to update the tnode
|
|
+ yaffs_DeleteChunk(dev,existingChunk,1,__LINE__);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // Backward scanning or we want to use the existing one
|
|
+ // Use existing.
|
|
+ // Delete the new one and return early so that the tnode isn't changed
|
|
+ yaffs_DeleteChunk(dev,chunkInNAND,1,__LINE__);
|
|
+ return YAFFS_OK;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ if(existingChunk == 0)
|
|
+ {
|
|
+ in->nDataChunks++;
|
|
+ }
|
|
+
|
|
+ tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = (chunkInNAND >> dev->chunkGroupBits);
|
|
+
|
|
+ return YAFFS_OK;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)
|
|
+{
|
|
+ int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL);
|
|
+
|
|
+ if(chunkInNAND >= 0)
|
|
+ {
|
|
+ return yaffs_ReadChunkWithTagsFromNAND(in->myDev,chunkInNAND,buffer,NULL);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ T(YAFFS_TRACE_NANDACCESS,(TSTR("Chunk %d not found zero instead" TENDSTR),chunkInNAND));
|
|
+
|
|
+ memset(buffer,0,in->myDev->nBytesPerChunk); // get sane data if you read a hole
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND,int lyn)
|
|
+{
|
|
+ int block;
|
|
+ int page;
|
|
+ yaffs_ExtendedTags tags;
|
|
+ yaffs_BlockInfo *bi;
|
|
+
|
|
+ if(chunkId <= 0) return;
|
|
+
|
|
+ dev->nDeletions++;
|
|
+ block = chunkId / dev->nChunksPerBlock;
|
|
+ page = chunkId % dev->nChunksPerBlock;
|
|
+
|
|
+ bi = yaffs_GetBlockInfo(dev,block);
|
|
+
|
|
+ T(YAFFS_TRACE_DELETION,(TSTR("line %d delete of chunk %d" TENDSTR),lyn,chunkId));
|
|
+
|
|
+ if(markNAND &&
|
|
+ bi->blockState != YAFFS_BLOCK_STATE_COLLECTING &&
|
|
+ !dev->isYaffs2)
|
|
+ {
|
|
+// yaffs_SpareInitialise(&spare);
|
|
+
|
|
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
|
|
+
|
|
+ //read data before write, to ensure correct ecc
|
|
+ //if we're using MTD verification under Linux
|
|
+// yaffs_ReadChunkFromNAND(dev,chunkId,NULL,&spare,0);
|
|
+#endif
|
|
+
|
|
+ yaffs_InitialiseTags(&tags);
|
|
+
|
|
+ tags.chunkDeleted = 1;
|
|
+
|
|
+
|
|
+ yaffs_WriteChunkWithTagsToNAND(dev,chunkId,NULL,&tags);
|
|
+ yaffs_HandleUpdateChunk(dev,chunkId,&tags);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ dev->nUnmarkedDeletions++;
|
|
+ }
|
|
+
|
|
+
|
|
+ // Pull out of the management area.
|
|
+ // If the whole block became dirty, this will kick off an erasure.
|
|
+ if( bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
|
|
+ bi->blockState == YAFFS_BLOCK_STATE_FULL ||
|
|
+ bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
|
|
+ bi->blockState == YAFFS_BLOCK_STATE_COLLECTING)
|
|
+ {
|
|
+ dev->nFreeChunks++;
|
|
+
|
|
+ yaffs_ClearChunkBit(dev,block,page);
|
|
+
|
|
+ bi->pagesInUse--;
|
|
+
|
|
+ if(bi->pagesInUse == 0 &&
|
|
+ !bi->hasShrinkHeader &&
|
|
+ bi->blockState != YAFFS_BLOCK_STATE_ALLOCATING &&
|
|
+ bi->blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING)
|
|
+ {
|
|
+ yaffs_BlockBecameDirty(dev,block);
|
|
+ }
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // T(("Bad news deleting chunk %d\n",chunkId));
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+static 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_ExtendedTags prevTags;
|
|
+
|
|
+ int newChunkId;
|
|
+ yaffs_ExtendedTags 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
|
|
+ yaffs_InitialiseTags(&newTags);
|
|
+
|
|
+ newTags.chunkId = chunkInInode;
|
|
+ newTags.objectId = in->objectId;
|
|
+ newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;
|
|
+ newTags.byteCount = nBytes;
|
|
+
|
|
+// yaffs_CalcTagsECC(&newTags);
|
|
+
|
|
+ newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);
|
|
+
|
|
+ if(newChunkId >= 0)
|
|
+ {
|
|
+ yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);
|
|
+
|
|
+
|
|
+ if(prevChunkId >= 0)
|
|
+ {
|
|
+ yaffs_DeleteChunk(dev,prevChunkId,1,__LINE__);
|
|
+
|
|
+ }
|
|
+
|
|
+ yaffs_CheckFileSanity(in);
|
|
+ }
|
|
+ return newChunkId;
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+// 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 YCHAR *name, int force,int isShrink,int shadows)
|
|
+{
|
|
+
|
|
+ yaffs_BlockInfo *bi;
|
|
+
|
|
+ yaffs_Device *dev = in->myDev;
|
|
+
|
|
+ int prevChunkId;
|
|
+ int retVal = 0;
|
|
+
|
|
+ int newChunkId;
|
|
+ yaffs_ExtendedTags newTags;
|
|
+
|
|
+ __u8 *buffer = NULL;
|
|
+ YCHAR oldName[YAFFS_MAX_NAME_LENGTH+1];
|
|
+
|
|
+ // __u8 bufferOld[YAFFS_BYTES_PER_CHUNK];
|
|
+
|
|
+ yaffs_ObjectHeader *oh = NULL;
|
|
+ // yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;
|
|
+
|
|
+
|
|
+ if(!in->fake || force)
|
|
+ {
|
|
+
|
|
+ yaffs_CheckGarbageCollection(dev);
|
|
+
|
|
+ buffer = yaffs_GetTempBuffer(in->myDev,__LINE__);
|
|
+ oh = (yaffs_ObjectHeader *)buffer;
|
|
+
|
|
+ prevChunkId = in->chunkId;
|
|
+
|
|
+ if(prevChunkId >= 0)
|
|
+ {
|
|
+ yaffs_ReadChunkWithTagsFromNAND(dev,prevChunkId,buffer,NULL);
|
|
+ memcpy(oldName,oh->name,sizeof(oh->name));
|
|
+ }
|
|
+
|
|
+ memset(buffer,0xFF,dev->nBytesPerChunk);
|
|
+
|
|
+ // Header data
|
|
+ oh->type = in->variantType;
|
|
+
|
|
+ oh->yst_mode = in->yst_mode;
|
|
+
|
|
+ // shadowing
|
|
+ oh->shadowsObject = shadows;
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ oh->win_atime[0] = in->win_atime[0];
|
|
+ oh->win_ctime[0] = in->win_ctime[0];
|
|
+ oh->win_mtime[0] = in->win_mtime[0];
|
|
+ oh->win_atime[1] = in->win_atime[1];
|
|
+ oh->win_ctime[1] = in->win_ctime[1];
|
|
+ oh->win_mtime[1] = in->win_mtime[1];
|
|
+#else
|
|
+ oh->yst_uid = in->yst_uid;
|
|
+ oh->yst_gid = in->yst_gid;
|
|
+ oh->yst_atime = in->yst_atime;
|
|
+ oh->yst_mtime = in->yst_mtime;
|
|
+ oh->yst_ctime = in->yst_ctime;
|
|
+ oh->yst_rdev = in->yst_rdev;
|
|
+#endif
|
|
+ if(in->parent)
|
|
+ {
|
|
+ oh->parentObjectId = in->parent->objectId;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ oh->parentObjectId = 0;
|
|
+ }
|
|
+
|
|
+ //oh->sum = in->sum;
|
|
+ if(name && *name)
|
|
+ {
|
|
+ memset(oh->name,0,sizeof(oh->name));
|
|
+ yaffs_strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);
|
|
+ }
|
|
+ else if(prevChunkId)
|
|
+ {
|
|
+ memcpy(oh->name, oldName,sizeof(oh->name));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ memset(oh->name,0,sizeof(oh->name));
|
|
+ }
|
|
+
|
|
+ oh->isShrink = isShrink;
|
|
+
|
|
+ switch(in->variantType)
|
|
+ {
|
|
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
|
|
+ // Should not happen
|
|
+ break;
|
|
+ case YAFFS_OBJECT_TYPE_FILE:
|
|
+ oh->fileSize = (oh->parentObjectId == YAFFS_OBJECTID_DELETED ||
|
|
+ oh->parentObjectId == YAFFS_OBJECTID_UNLINKED) ? 0 : 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:
|
|
+ yaffs_strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH);
|
|
+ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ // Tags
|
|
+ yaffs_InitialiseTags(&newTags);
|
|
+ in->serial++;
|
|
+ newTags.chunkId = 0;
|
|
+ newTags.objectId = in->objectId;
|
|
+ newTags.serialNumber = in->serial;
|
|
+
|
|
+ // Add extra info for file header
|
|
+
|
|
+ newTags.extraHeaderInfoAvailable = 1;
|
|
+ newTags.extraParentObjectId = oh->parentObjectId;
|
|
+ newTags.extraFileLength = oh->fileSize;
|
|
+ newTags.extraIsShrinkHeader = oh->isShrink;
|
|
+ newTags.extraEquivalentObjectId = oh->equivalentObjectId;
|
|
+ newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0;
|
|
+ newTags.extraObjectType = in->variantType;
|
|
+
|
|
+ // Create new chunk in NAND
|
|
+ newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags, (prevChunkId >= 0) ? 1 : 0 );
|
|
+
|
|
+ if(newChunkId >= 0)
|
|
+ {
|
|
+
|
|
+ in->chunkId = newChunkId;
|
|
+
|
|
+ if(prevChunkId >= 0)
|
|
+ {
|
|
+ yaffs_DeleteChunk(dev,prevChunkId,1,__LINE__);
|
|
+ }
|
|
+
|
|
+ in->dirty = 0;
|
|
+
|
|
+ // If this was a shrink, then mark the block that the chunk lives on
|
|
+ if(isShrink)
|
|
+ {
|
|
+ bi = yaffs_GetBlockInfo(in->myDev,newChunkId / in->myDev->nChunksPerBlock);
|
|
+ bi->hasShrinkHeader = 1;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ retVal = newChunkId;
|
|
+
|
|
+ }
|
|
+
|
|
+ if(buffer)
|
|
+ yaffs_ReleaseTempBuffer(dev,buffer,__LINE__);
|
|
+
|
|
+ return retVal;
|
|
+}
|
|
+
|
|
+
|
|
+/////////////////////// Short Operations Cache ////////////////////////////////
|
|
+// In many siturations where there is no high level buffering (eg WinCE) a lot of
|
|
+// reads might be short sequential reads, and a lot of writes may be short
|
|
+// sequential writes. eg. scanning/writing a jpeg file.
|
|
+// In these cases, a short read/write cache can provide a huge perfomance benefit
|
|
+// with dumb-as-a-rock code.
|
|
+// There are a limited number (~10) of cache chunks per device so that we don't
|
|
+// need a very intelligent search.
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
|
|
+{
|
|
+ yaffs_Device *dev = obj->myDev;
|
|
+ int lowest = -99; // Stop compiler whining.
|
|
+ int i;
|
|
+ yaffs_ChunkCache *cache;
|
|
+ int chunkWritten = 0;
|
|
+ //int nBytes;
|
|
+ int nCaches = obj->myDev->nShortOpCaches;
|
|
+
|
|
+ if (nCaches > 0)
|
|
+ {
|
|
+ do{
|
|
+ cache = NULL;
|
|
+
|
|
+ // Find the dirty cache for this object with the lowest chunk id.
|
|
+ for(i = 0; i < nCaches; i++)
|
|
+ {
|
|
+ if(dev->srCache[i].object == obj &&
|
|
+ dev->srCache[i].dirty)
|
|
+ {
|
|
+ if(!cache || dev->srCache[i].chunkId < lowest)
|
|
+ {
|
|
+ cache = &dev->srCache[i];
|
|
+ lowest = cache->chunkId;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(cache && !cache->locked)
|
|
+ {
|
|
+ //Write it out and free it up
|
|
+
|
|
+#if 0
|
|
+ nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);
|
|
+
|
|
+ if(nBytes > YAFFS_BYTES_PER_CHUNK)
|
|
+ {
|
|
+ nBytes= YAFFS_BYTES_PER_CHUNK;
|
|
+ }
|
|
+#endif
|
|
+ chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
|
|
+ cache->chunkId,
|
|
+ cache->data,
|
|
+ cache->nBytes,1);
|
|
+
|
|
+ cache->dirty = 0;
|
|
+ cache->object = NULL;
|
|
+ }
|
|
+
|
|
+ } while(cache && chunkWritten > 0);
|
|
+
|
|
+ if(cache)
|
|
+ {
|
|
+ //Hoosterman, disk full while writing cache out.
|
|
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during cache write" TENDSTR)));
|
|
+
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+// Grab us a chunk for use.
|
|
+// First look for an empty one.
|
|
+// Then look for the least recently used non-dirty one.
|
|
+// Then look for the least recently used dirty one...., flush and look again.
|
|
+static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device *dev)
|
|
+{
|
|
+ int i;
|
|
+ int usage;
|
|
+ int theOne;
|
|
+
|
|
+ if(dev->nShortOpCaches > 0)
|
|
+ {
|
|
+ for(i = 0; i < dev->nShortOpCaches; i++)
|
|
+ {
|
|
+ if(!dev->srCache[i].object)
|
|
+ {
|
|
+ //T(("Grabbing empty %d\n",i));
|
|
+
|
|
+ //printf("Grabbing empty %d\n",i);
|
|
+
|
|
+ return &dev->srCache[i];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+
|
|
+ theOne = -1;
|
|
+ usage = 0; // just to stop the compiler grizzling
|
|
+
|
|
+ for(i = 0; i < dev->nShortOpCaches; i++)
|
|
+ {
|
|
+ if(!dev->srCache[i].dirty &&
|
|
+ ((dev->srCache[i].lastUse < usage && theOne >= 0)||
|
|
+ theOne < 0))
|
|
+ {
|
|
+ usage = dev->srCache[i].lastUse;
|
|
+ theOne = i;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ //T(("Grabbing non-empty %d\n",theOne));
|
|
+
|
|
+ //if(theOne >= 0) printf("Grabbed non-empty cache %d\n",theOne);
|
|
+
|
|
+ return theOne >= 0 ? &dev->srCache[theOne] : NULL;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)
|
|
+{
|
|
+ yaffs_ChunkCache *cache;
|
|
+ yaffs_Object *theObj;
|
|
+ int usage;
|
|
+ int i;
|
|
+ int pushout;
|
|
+
|
|
+ if(dev->nShortOpCaches > 0)
|
|
+ {
|
|
+ // Try find a non-dirty one...
|
|
+
|
|
+ cache = yaffs_GrabChunkCacheWorker(dev);
|
|
+
|
|
+ if(!cache)
|
|
+ {
|
|
+ // They were all dirty, find the last recently used object and flush
|
|
+ // its cache, then find again.
|
|
+ // NB what's here is not very accurate, we actually flush the object
|
|
+ // the last recently used page.
|
|
+
|
|
+ // With locking we can't assume we can use entry zero
|
|
+
|
|
+
|
|
+ theObj = NULL;
|
|
+ usage = -1;
|
|
+ cache = NULL;
|
|
+ pushout = -1;
|
|
+
|
|
+ for(i = 0; i < dev->nShortOpCaches; i++)
|
|
+ {
|
|
+ if( dev->srCache[i].object &&
|
|
+ !dev->srCache[i].locked &&
|
|
+ (dev->srCache[i].lastUse < usage || !cache))
|
|
+ {
|
|
+ usage = dev->srCache[i].lastUse;
|
|
+ theObj = dev->srCache[i].object;
|
|
+ cache = &dev->srCache[i];
|
|
+ pushout = i;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(!cache || cache->dirty)
|
|
+ {
|
|
+
|
|
+ //printf("Dirty ");
|
|
+ yaffs_FlushFilesChunkCache(theObj);
|
|
+
|
|
+ // Try again
|
|
+ cache = yaffs_GrabChunkCacheWorker(dev);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ //printf(" pushout %d\n",pushout);
|
|
+ }
|
|
+
|
|
+ }
|
|
+ return cache;
|
|
+ }
|
|
+ else
|
|
+ return NULL;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+// Find a cached chunk
|
|
+static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object *obj, int chunkId)
|
|
+{
|
|
+ yaffs_Device *dev = obj->myDev;
|
|
+ int i;
|
|
+ if(dev->nShortOpCaches > 0)
|
|
+ {
|
|
+ for(i = 0; i < dev->nShortOpCaches; i++)
|
|
+ {
|
|
+ if(dev->srCache[i].object == obj &&
|
|
+ dev->srCache[i].chunkId == chunkId)
|
|
+ {
|
|
+ dev->cacheHits++;
|
|
+
|
|
+ return &dev->srCache[i];
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+// Mark the chunk for the least recently used algorithym
|
|
+static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache, int isAWrite)
|
|
+{
|
|
+
|
|
+ if(dev->nShortOpCaches > 0)
|
|
+ {
|
|
+ if( dev->srLastUse < 0 ||
|
|
+ dev->srLastUse > 100000000)
|
|
+ {
|
|
+ // Reset the cache usages
|
|
+ int i;
|
|
+ for(i = 1; i < dev->nShortOpCaches; i++)
|
|
+ {
|
|
+ dev->srCache[i].lastUse = 0;
|
|
+ }
|
|
+ dev->srLastUse = 0;
|
|
+ }
|
|
+
|
|
+ dev->srLastUse++;
|
|
+
|
|
+ cache->lastUse = dev->srLastUse;
|
|
+
|
|
+ if(isAWrite)
|
|
+ {
|
|
+ cache->dirty = 1;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+// Invalidate a single cache page.
|
|
+// Do this when a whole page gets written,
|
|
+// ie the short cache for this page is no longer valid.
|
|
+static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId)
|
|
+{
|
|
+ if(object->myDev->nShortOpCaches > 0)
|
|
+ {
|
|
+ yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId);
|
|
+
|
|
+ if(cache)
|
|
+ {
|
|
+ cache->object = NULL;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+// Invalidate all the cache pages associated with this object
|
|
+// Do this whenever ther file is deleted or resized.
|
|
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in)
|
|
+{
|
|
+ int i;
|
|
+ yaffs_Device *dev = in->myDev;
|
|
+
|
|
+ if(dev->nShortOpCaches > 0)
|
|
+ {
|
|
+ // Now invalidate it.
|
|
+ for(i = 0; i < dev->nShortOpCaches; i++)
|
|
+ {
|
|
+ if(dev->srCache[i].object == in)
|
|
+ {
|
|
+ dev->srCache[i].object = NULL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+///////////////////////// 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)
|
|
+{
|
|
+
|
|
+
|
|
+ int chunk;
|
|
+ int start;
|
|
+ int nToCopy;
|
|
+ int n = nBytes;
|
|
+ int nDone = 0;
|
|
+ yaffs_ChunkCache *cache;
|
|
+
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+ dev = in->myDev;
|
|
+
|
|
+ while(n > 0)
|
|
+ {
|
|
+ chunk = offset / dev->nBytesPerChunk + 1; // The first chunk is 1
|
|
+ start = offset % dev->nBytesPerChunk;
|
|
+
|
|
+ // OK now check for the curveball where the start and end are in
|
|
+ // the same chunk.
|
|
+ if( (start + n) < dev->nBytesPerChunk)
|
|
+ {
|
|
+ nToCopy = n;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nToCopy = dev->nBytesPerChunk - start;
|
|
+ }
|
|
+
|
|
+ cache = yaffs_FindChunkCache(in,chunk);
|
|
+
|
|
+ // If the chunk is already in the cache or it is less than a whole chunk
|
|
+ // then use the cache (if there is caching)
|
|
+ // else bypass the cache.
|
|
+ if( cache || nToCopy != dev->nBytesPerChunk)
|
|
+ {
|
|
+ if(dev->nShortOpCaches > 0)
|
|
+ {
|
|
+
|
|
+ // If we can't find the data in the cache, then load it up.
|
|
+
|
|
+ if(!cache)
|
|
+ {
|
|
+ cache = yaffs_GrabChunkCache(in->myDev);
|
|
+ cache->object = in;
|
|
+ cache->chunkId = chunk;
|
|
+ cache->dirty = 0;
|
|
+ cache->locked = 0;
|
|
+ yaffs_ReadChunkDataFromObject(in,chunk,cache->data);
|
|
+ cache->nBytes = 0;
|
|
+ }
|
|
+
|
|
+ yaffs_UseChunkCache(dev,cache,0);
|
|
+
|
|
+ cache->locked = 1;
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_UnlockYAFFS(TRUE);
|
|
+#endif
|
|
+ memcpy(buffer,&cache->data[start],nToCopy);
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_LockYAFFS(TRUE);
|
|
+#endif
|
|
+ cache->locked = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // Read into the local buffer then copy...
|
|
+
|
|
+ __u8 *localBuffer = yaffs_GetTempBuffer(dev,__LINE__);
|
|
+ yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_UnlockYAFFS(TRUE);
|
|
+#endif
|
|
+ memcpy(buffer,&localBuffer[start],nToCopy);
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_LockYAFFS(TRUE);
|
|
+#endif
|
|
+ yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
|
|
+ }
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ __u8 *localBuffer = yaffs_GetTempBuffer(dev,__LINE__);
|
|
+
|
|
+ // Under WinCE can't do direct transfer. Need to use a local buffer.
|
|
+ // This is because we otherwise screw up WinCE's memory mapper
|
|
+ yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_UnlockYAFFS(TRUE);
|
|
+#endif
|
|
+ memcpy(buffer,localBuffer,dev->nBytesPerChunk);
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_LockYAFFS(TRUE);
|
|
+ yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
|
|
+#endif
|
|
+
|
|
+#else
|
|
+ // A full chunk. Read directly into the supplied buffer.
|
|
+ yaffs_ReadChunkDataFromObject(in,chunk,buffer);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ n -= nToCopy;
|
|
+ offset += nToCopy;
|
|
+ buffer += nToCopy;
|
|
+ nDone += nToCopy;
|
|
+
|
|
+ }
|
|
+
|
|
+ return nDone;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes, int writeThrough)
|
|
+{
|
|
+
|
|
+ int chunk;
|
|
+ int start;
|
|
+ int nToCopy;
|
|
+ int n = nBytes;
|
|
+ int nDone = 0;
|
|
+ int nToWriteBack;
|
|
+ int startOfWrite = offset;
|
|
+ int chunkWritten = 0;
|
|
+ int nBytesRead;
|
|
+
|
|
+ yaffs_Device *dev;
|
|
+
|
|
+ dev = in->myDev;
|
|
+
|
|
+
|
|
+ while(n > 0 && chunkWritten >= 0)
|
|
+ {
|
|
+ chunk = offset / dev->nBytesPerChunk + 1;
|
|
+ start = offset % dev->nBytesPerChunk;
|
|
+
|
|
+
|
|
+ // OK now check for the curveball where the start and end are in
|
|
+ // the same chunk.
|
|
+
|
|
+ if((start + n) < dev->nBytesPerChunk)
|
|
+ {
|
|
+ nToCopy = n;
|
|
+
|
|
+ // Now folks, to calculate how many bytes to write back....
|
|
+ // If we're overwriting and not writing to then end of file then
|
|
+ // we need to write back as much as was there before.
|
|
+
|
|
+ nBytesRead = in->variant.fileVariant.fileSize - ((chunk -1) * dev->nBytesPerChunk);
|
|
+
|
|
+ if(nBytesRead > dev->nBytesPerChunk)
|
|
+ {
|
|
+ nBytesRead = dev->nBytesPerChunk;
|
|
+ }
|
|
+
|
|
+ nToWriteBack = (nBytesRead > (start + n)) ? nBytesRead : (start +n);
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nToCopy = dev->nBytesPerChunk - start;
|
|
+ nToWriteBack = dev->nBytesPerChunk;
|
|
+ }
|
|
+
|
|
+ if(nToCopy != dev->nBytesPerChunk)
|
|
+ {
|
|
+ // An incomplete start or end chunk (or maybe both start and end chunk)
|
|
+ if(dev->nShortOpCaches > 0)
|
|
+ {
|
|
+ yaffs_ChunkCache *cache;
|
|
+ // If we can't find the data in the cache, then load it up.
|
|
+ cache = yaffs_FindChunkCache(in,chunk);
|
|
+ if(!cache && yaffs_CheckSpaceForAllocation(in->myDev))
|
|
+ {
|
|
+ cache = yaffs_GrabChunkCache(in->myDev);
|
|
+ cache->object = in;
|
|
+ cache->chunkId = chunk;
|
|
+ cache->dirty = 0;
|
|
+ cache->locked = 0;
|
|
+ yaffs_ReadChunkDataFromObject(in,chunk,cache->data);
|
|
+ }
|
|
+
|
|
+ if(cache)
|
|
+ {
|
|
+ yaffs_UseChunkCache(dev,cache,1);
|
|
+ cache->locked = 1;
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_UnlockYAFFS(TRUE);
|
|
+#endif
|
|
+
|
|
+ memcpy(&cache->data[start],buffer,nToCopy);
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_LockYAFFS(TRUE);
|
|
+#endif
|
|
+ cache->locked = 0;
|
|
+ cache->nBytes = nToWriteBack;
|
|
+
|
|
+ if(writeThrough)
|
|
+ {
|
|
+ chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
|
|
+ cache->chunkId,
|
|
+ cache->data,
|
|
+ cache->nBytes,1);
|
|
+ cache->dirty = 0;
|
|
+ }
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ chunkWritten = -1; // fail the write
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // 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.
|
|
+
|
|
+ __u8 *localBuffer = yaffs_GetTempBuffer(dev,__LINE__);
|
|
+
|
|
+ yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_UnlockYAFFS(TRUE);
|
|
+#endif
|
|
+
|
|
+ memcpy(&localBuffer[start],buffer,nToCopy);
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_LockYAFFS(TRUE);
|
|
+#endif
|
|
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0);
|
|
+
|
|
+ yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
|
|
+
|
|
+ //T(("Write with readback to chunk %d %d start %d copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));
|
|
+ }
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ // Under WinCE can't do direct transfer. Need to use a local buffer.
|
|
+ // This is because we otherwise screw up WinCE's memory mapper
|
|
+ __u8 *localBuffer = yaffs_GetTempBuffer(dev,__LINE__);
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_UnlockYAFFS(TRUE);
|
|
+#endif
|
|
+ memcpy(localBuffer,buffer,dev->nBytesPerChunk);
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_LockYAFFS(TRUE);
|
|
+#endif
|
|
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,dev->nBytesPerChunk,0);
|
|
+ yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
|
|
+#else
|
|
+ // A full chunk. Write directly from the supplied buffer.
|
|
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,dev->nBytesPerChunk,0);
|
|
+#endif
|
|
+ // Since we've overwritten the cached data, we better invalidate it.
|
|
+ yaffs_InvalidateChunkCache(in,chunk);
|
|
+ //T(("Write to chunk %d %d\n",chunk,chunkWritten));
|
|
+ }
|
|
+
|
|
+ if(chunkWritten >= 0)
|
|
+ {
|
|
+ n -= nToCopy;
|
|
+ offset += nToCopy;
|
|
+ buffer += nToCopy;
|
|
+ nDone += nToCopy;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ // Update file object
|
|
+
|
|
+ if((startOfWrite + nDone) > in->variant.fileVariant.fileSize)
|
|
+ {
|
|
+ in->variant.fileVariant.fileSize = (startOfWrite + nDone);
|
|
+ }
|
|
+
|
|
+ in->dirty = 1;
|
|
+
|
|
+ return nDone;
|
|
+}
|
|
+
|
|
+static void yaffs_PruneResizedChunks(yaffs_Object *in, int newSize)
|
|
+{
|
|
+
|
|
+ yaffs_Device *dev = in->myDev;
|
|
+ int oldFileSize = in->variant.fileVariant.fileSize;
|
|
+
|
|
+
|
|
+ int lastDel = 1 + (oldFileSize-1)/dev->nBytesPerChunk;
|
|
+
|
|
+ int startDel = 1 + (newSize + dev->nBytesPerChunk - 1)/
|
|
+ dev->nBytesPerChunk;
|
|
+ int i;
|
|
+ int chunkId;
|
|
+
|
|
+ // 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)
|
|
+ {
|
|
+ if(chunkId < (dev->internalStartBlock * dev->nChunksPerBlock) ||
|
|
+ chunkId >= ((dev->internalEndBlock+1) * dev->nChunksPerBlock))
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("Found daft chunkId %d for %d"TENDSTR),chunkId,i));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ in->nDataChunks--;
|
|
+ yaffs_DeleteChunk(dev,chunkId,1,__LINE__);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+int yaffs_ResizeFile(yaffs_Object *in, int newSize)
|
|
+{
|
|
+
|
|
+ int oldFileSize = in->variant.fileVariant.fileSize;
|
|
+ int sizeOfPartialChunk;
|
|
+ yaffs_Device *dev = in->myDev;
|
|
+
|
|
+ sizeOfPartialChunk = newSize % dev->nBytesPerChunk;
|
|
+
|
|
+
|
|
+ yaffs_FlushFilesChunkCache(in);
|
|
+ yaffs_InvalidateWholeChunkCache(in);
|
|
+
|
|
+ yaffs_CheckGarbageCollection(dev);
|
|
+
|
|
+ if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
|
|
+ {
|
|
+ return yaffs_GetFileSize(in);
|
|
+ }
|
|
+
|
|
+ if(newSize < oldFileSize)
|
|
+ {
|
|
+
|
|
+ yaffs_PruneResizedChunks(in,newSize);
|
|
+
|
|
+ if(sizeOfPartialChunk != 0)
|
|
+ {
|
|
+ int lastChunk = 1+ newSize/dev->nBytesPerChunk;
|
|
+ __u8 *localBuffer = yaffs_GetTempBuffer(dev,__LINE__);
|
|
+
|
|
+ // Got to read and rewrite the last chunk with its new size and zero pad
|
|
+ yaffs_ReadChunkDataFromObject(in,lastChunk,localBuffer);
|
|
+
|
|
+ memset(localBuffer + sizeOfPartialChunk,0, dev->nBytesPerChunk - sizeOfPartialChunk);
|
|
+
|
|
+ yaffs_WriteChunkDataToObject(in,lastChunk,localBuffer,sizeOfPartialChunk,1);
|
|
+
|
|
+ yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
|
|
+ }
|
|
+
|
|
+ in->variant.fileVariant.fileSize = newSize;
|
|
+
|
|
+ yaffs_PruneFileStructure(dev,&in->variant.fileVariant);
|
|
+
|
|
+ // Write a new object header to show we've shrunk the file
|
|
+ // Do this only if the file is not in the deleted directories.
|
|
+ if(in->parent->objectId != YAFFS_OBJECTID_UNLINKED &&
|
|
+ in->parent->objectId != YAFFS_OBJECTID_DELETED
|
|
+ )
|
|
+ {
|
|
+ yaffs_UpdateObjectHeader(in,NULL, 0, 1,0);
|
|
+ }
|
|
+
|
|
+
|
|
+ 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 yaffs_strlen(obj->variant.symLinkVariant.alias);
|
|
+ default:
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+// yaffs_FlushFile() updates the file's
|
|
+// objectId in NAND
|
|
+
|
|
+int yaffs_FlushFile(yaffs_Object *in, int updateTime)
|
|
+{
|
|
+ int retVal;
|
|
+ if(in->dirty)
|
|
+ {
|
|
+ //T(("flushing object header\n"));
|
|
+
|
|
+ yaffs_FlushFilesChunkCache(in);
|
|
+ if(updateTime)
|
|
+ {
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ yfsd_WinFileTimeNow(in->win_mtime);
|
|
+#else
|
|
+
|
|
+ in->yst_mtime = Y_CURRENT_TIME;
|
|
+
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ retVal = (yaffs_UpdateObjectHeader(in,NULL,0,0,0) >= 0)? YAFFS_OK : YAFFS_FAIL;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ retVal = YAFFS_OK;
|
|
+ }
|
|
+
|
|
+ return retVal;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)
|
|
+{
|
|
+
|
|
+ // First off, invalidate the file's data in the cache, without flushing.
|
|
+ yaffs_InvalidateWholeChunkCache(in);
|
|
+
|
|
+ if(in->myDev->isYaffs2 && (in->parent != in->myDev->deletedDir))
|
|
+ {
|
|
+ // Move to the unlinked directory so we have a record that it was deleted.
|
|
+ yaffs_ChangeObjectName(in, in->myDev->deletedDir,NULL,0,0);
|
|
+
|
|
+ }
|
|
+
|
|
+
|
|
+ yaffs_RemoveObjectFromDirectory(in);
|
|
+ yaffs_DeleteChunk(in->myDev,in->chunkId,1,__LINE__);
|
|
+ in->chunkId = -1;
|
|
+#if 0
|
|
+#ifdef __KERNEL__
|
|
+ if(in->myInode)
|
|
+ {
|
|
+ in->myInode->u.generic_ip = NULL;
|
|
+ in->myInode = 0;
|
|
+ }
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+ 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_UnlinkFile(yaffs_Object *in)
|
|
+{
|
|
+
|
|
+#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND_DELETION
|
|
+
|
|
+ // Delete the file data & tnodes
|
|
+
|
|
+ yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL);
|
|
+
|
|
+
|
|
+ yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
|
|
+
|
|
+ return yaffs_DoGenericObjectDeletion(in);
|
|
+#else
|
|
+ int retVal;
|
|
+ int immediateDeletion=0;
|
|
+
|
|
+ if(1)
|
|
+ {
|
|
+ //in->unlinked = 1;
|
|
+ //in->myDev->nUnlinkedFiles++;
|
|
+ //in->renameAllowed = 0;
|
|
+#ifdef __KERNEL__
|
|
+ if(!in->myInode)
|
|
+ {
|
|
+ immediateDeletion = 1;
|
|
+
|
|
+ }
|
|
+#else
|
|
+ if(in->inUse <= 0)
|
|
+ {
|
|
+ immediateDeletion = 1;
|
|
+
|
|
+ }
|
|
+#endif
|
|
+ if(immediateDeletion)
|
|
+ {
|
|
+ retVal = yaffs_ChangeObjectName(in, in->myDev->deletedDir,NULL,0,0);
|
|
+ T(YAFFS_TRACE_TRACING,(TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId));
|
|
+ in->deleted=1;
|
|
+ in->myDev->nDeletedFiles++;
|
|
+ if( 0 && in->myDev->isYaffs2)
|
|
+ {
|
|
+ yaffs_ResizeFile(in,0);
|
|
+ }
|
|
+ yaffs_SoftDeleteFile(in);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0,0);
|
|
+ }
|
|
+
|
|
+ }
|
|
+ return retVal;
|
|
+
|
|
+
|
|
+#endif
|
|
+}
|
|
+
|
|
+int yaffs_DeleteFile(yaffs_Object *in)
|
|
+{
|
|
+ int retVal = YAFFS_OK;
|
|
+
|
|
+ if(in->nDataChunks > 0)
|
|
+ {
|
|
+ // Use soft deletion
|
|
+ if(!in->unlinked)
|
|
+ {
|
|
+ retVal = yaffs_UnlinkFile(in);
|
|
+ }
|
|
+ if(retVal == YAFFS_OK &&
|
|
+ in->unlinked &&
|
|
+ !in->deleted)
|
|
+ {
|
|
+ in->deleted = 1;
|
|
+ in->myDev->nDeletedFiles++;
|
|
+ yaffs_SoftDeleteFile(in);
|
|
+ }
|
|
+ return in->deleted ? YAFFS_OK : YAFFS_FAIL;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // The file has no data chunks so we toss it immediately
|
|
+ yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
|
|
+ in->variant.fileVariant.top = NULL;
|
|
+ yaffs_DoGenericObjectDeletion(in);
|
|
+
|
|
+ return YAFFS_OK;
|
|
+ }
|
|
+}
|
|
+
|
|
+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 void yaffs_DestroyObject(yaffs_Object *obj)
|
|
+{
|
|
+ switch(obj->variantType)
|
|
+ {
|
|
+ case YAFFS_OBJECT_TYPE_FILE: yaffs_DeleteFile(obj); break;
|
|
+ case YAFFS_OBJECT_TYPE_DIRECTORY: yaffs_DeleteDirectory(obj); break;
|
|
+ case YAFFS_OBJECT_TYPE_SYMLINK: yaffs_DeleteSymLink(obj); break;
|
|
+ case YAFFS_OBJECT_TYPE_HARDLINK: yaffs_DeleteHardLink(obj); break;
|
|
+ case YAFFS_OBJECT_TYPE_SPECIAL: yaffs_DoGenericObjectDeletion(obj); break;
|
|
+ case YAFFS_OBJECT_TYPE_UNKNOWN: break; // should not happen.
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static int yaffs_UnlinkWorker(yaffs_Object *obj)
|
|
+{
|
|
+
|
|
+
|
|
+ if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
|
|
+ {
|
|
+ return yaffs_DeleteHardLink(obj);
|
|
+ }
|
|
+ else if(!list_empty(&obj->hardLinks))
|
|
+ {
|
|
+ // 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;
|
|
+ YCHAR 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,0,0);
|
|
+
|
|
+ if(retVal == YAFFS_OK)
|
|
+ {
|
|
+ retVal = yaffs_DoGenericObjectDeletion(hl);
|
|
+ }
|
|
+ return retVal;
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ switch(obj->variantType)
|
|
+ {
|
|
+ case YAFFS_OBJECT_TYPE_FILE:
|
|
+ return yaffs_UnlinkFile(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_SPECIAL:
|
|
+ return yaffs_DoGenericObjectDeletion(obj);
|
|
+ break;
|
|
+ case YAFFS_OBJECT_TYPE_HARDLINK:
|
|
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
|
|
+ default:
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+int yaffs_Unlink(yaffs_Object *dir, const YCHAR *name)
|
|
+{
|
|
+ yaffs_Object *obj;
|
|
+
|
|
+ obj = yaffs_FindObjectByName(dir,name);
|
|
+
|
|
+ if(obj && obj->unlinkAllowed)
|
|
+ {
|
|
+ return yaffs_UnlinkWorker(obj);
|
|
+ }
|
|
+
|
|
+ return YAFFS_FAIL;
|
|
+
|
|
+}
|
|
+
|
|
+//////////////// Initialisation Scanning /////////////////
|
|
+
|
|
+
|
|
+void yaffs_HandleShadowedObject(yaffs_Device *dev, int objId, int backwardScanning)
|
|
+{
|
|
+ //Todo
|
|
+}
|
|
+
|
|
+#if 0
|
|
+// 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)
|
|
+{
|
|
+ yaffsExtendedTags *tags;
|
|
+
|
|
+ yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock,NULL,&tags,1);
|
|
+#if 1
|
|
+ if(yaffs_CountBits(spare.blockStatus) < 7)
|
|
+ {
|
|
+ return 1;
|
|
+ }
|
|
+#else
|
|
+ if(spare.blockStatus != 0xFF)
|
|
+ {
|
|
+ return 1;
|
|
+ }
|
|
+#endif
|
|
+ yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock + 1,NULL,&spare,1);
|
|
+
|
|
+#if 1
|
|
+ if(yaffs_CountBits(spare.blockStatus) < 7)
|
|
+ {
|
|
+ return 1;
|
|
+ }
|
|
+#else
|
|
+ if(spare.blockStatus != 0xFF)
|
|
+ {
|
|
+ return 1;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ int seq;
|
|
+ int block;
|
|
+} yaffs_BlockIndex;
|
|
+
|
|
+
|
|
+
|
|
+static int yaffs_Scan(yaffs_Device *dev)
|
|
+{
|
|
+ yaffs_ExtendedTags tags;
|
|
+ int blk;
|
|
+ int blockIterator;
|
|
+ int startIterator;
|
|
+ int endIterator;
|
|
+ int nBlocksToScan = 0;
|
|
+
|
|
+ int chunk;
|
|
+ int c;
|
|
+ int deleted;
|
|
+ yaffs_BlockState state;
|
|
+ yaffs_Object *hardList = NULL;
|
|
+ yaffs_Object *hl;
|
|
+ yaffs_BlockInfo *bi;
|
|
+ int sequenceNumber;
|
|
+ yaffs_ObjectHeader *oh;
|
|
+ yaffs_Object *in;
|
|
+ yaffs_Object *parent;
|
|
+ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
|
|
+
|
|
+ __u8 *chunkData;
|
|
+
|
|
+ yaffs_BlockIndex *blockIndex = NULL;
|
|
+
|
|
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_Scan starts intstartblk %d intendblk %d..." TENDSTR),dev->internalStartBlock,dev->internalEndBlock));
|
|
+
|
|
+ chunkData = yaffs_GetTempBuffer(dev,__LINE__);
|
|
+
|
|
+
|
|
+ dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
|
|
+
|
|
+ if(dev->isYaffs2)
|
|
+ {
|
|
+ blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));
|
|
+ }
|
|
+
|
|
+
|
|
+ // Scan all the blocks to determine their state
|
|
+ for(blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++)
|
|
+ {
|
|
+ bi = yaffs_GetBlockInfo(dev,blk);
|
|
+ yaffs_ClearChunkBits(dev,blk);
|
|
+ bi->pagesInUse = 0;
|
|
+ bi->softDeletions = 0;
|
|
+
|
|
+ yaffs_QueryInitialBlockState(dev,blk,&state,&sequenceNumber);
|
|
+
|
|
+ bi->blockState = state;
|
|
+ bi->sequenceNumber = sequenceNumber;
|
|
+
|
|
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block scanning block %d state %d seq %d" TENDSTR),blk,state,sequenceNumber));
|
|
+
|
|
+ if(state == YAFFS_BLOCK_STATE_DEAD)
|
|
+ {
|
|
+ T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));
|
|
+ }
|
|
+ else if(state == YAFFS_BLOCK_STATE_EMPTY)
|
|
+ {
|
|
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block empty " TENDSTR)));
|
|
+ dev->nErasedBlocks++;
|
|
+ dev->nFreeChunks += dev->nChunksPerBlock;
|
|
+ }
|
|
+ else if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
|
|
+ {
|
|
+
|
|
+ // Determine the highest sequence number
|
|
+ if( dev->isYaffs2 &&
|
|
+ sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
|
|
+ sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER)
|
|
+ {
|
|
+
|
|
+ blockIndex[nBlocksToScan].seq = sequenceNumber;
|
|
+ blockIndex[nBlocksToScan].block = blk;
|
|
+
|
|
+ nBlocksToScan++;
|
|
+
|
|
+ if(sequenceNumber >= dev->sequenceNumber)
|
|
+ {
|
|
+ dev->sequenceNumber = sequenceNumber;
|
|
+ }
|
|
+ }
|
|
+ else if(dev->isYaffs2)
|
|
+ {
|
|
+ // TODO: Nasty sequence number!
|
|
+ T(YAFFS_TRACE_SCAN,(TSTR("Block scanning block %d has bad sequence number %d" TENDSTR),blk,sequenceNumber));
|
|
+
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Sort the blocks
|
|
+ // Dungy old bubble sort for now...
|
|
+ if(dev->isYaffs2)
|
|
+ {
|
|
+ yaffs_BlockIndex temp;
|
|
+ int i;
|
|
+ int j;
|
|
+
|
|
+ for(i = 0; i < nBlocksToScan; i++)
|
|
+ for(j = i+1; j < nBlocksToScan; j++)
|
|
+ if(blockIndex[i].seq > blockIndex[j].seq)
|
|
+ {
|
|
+ temp = blockIndex[j];
|
|
+ blockIndex[j] = blockIndex[i];
|
|
+ blockIndex[i] = temp;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ // Now scan the blocks looking at the data.
|
|
+ if(dev->isYaffs2)
|
|
+ {
|
|
+ startIterator = 0;
|
|
+ endIterator = nBlocksToScan-1;
|
|
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("%d blocks to be scanned" TENDSTR),nBlocksToScan));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ startIterator = dev->internalStartBlock;
|
|
+ endIterator = dev->internalEndBlock;
|
|
+ }
|
|
+
|
|
+ // For each block....
|
|
+ for(blockIterator = startIterator; blockIterator <= endIterator; blockIterator++)
|
|
+ {
|
|
+
|
|
+ if(dev->isYaffs2)
|
|
+ {
|
|
+ // get the block to scan in the correct order
|
|
+ blk = blockIndex[blockIterator].block;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ blk = blockIterator;
|
|
+ }
|
|
+
|
|
+
|
|
+ bi = yaffs_GetBlockInfo(dev,blk);
|
|
+ state = bi->blockState;
|
|
+
|
|
+ deleted = 0;
|
|
+
|
|
+ // For each chunk in each block that needs scanning....
|
|
+ for(c = 0; c < dev->nChunksPerBlock &&
|
|
+ state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++)
|
|
+ {
|
|
+ // Read the tags and decide what to do
|
|
+ chunk = blk * dev->nChunksPerBlock + c;
|
|
+
|
|
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
|
|
+
|
|
+ // Let's have a good look at this chunk...
|
|
+
|
|
+
|
|
+ if(!dev->isYaffs2 && tags.chunkDeleted)
|
|
+ {
|
|
+ // YAFFS1 only...
|
|
+ // A deleted chunk
|
|
+ deleted++;
|
|
+ dev->nFreeChunks ++;
|
|
+ //T((" %d %d deleted\n",blk,c));
|
|
+ }
|
|
+ else if(!tags.chunkUsed)
|
|
+ {
|
|
+ // 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)
|
|
+ {
|
|
+ // We're looking at the first chunk in the block so the block is unused
|
|
+ state = YAFFS_BLOCK_STATE_EMPTY;
|
|
+ dev->nErasedBlocks++;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // this is the block being allocated from
|
|
+ T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));
|
|
+ state = YAFFS_BLOCK_STATE_ALLOCATING;
|
|
+ dev->allocationBlock = blk;
|
|
+ dev->allocationPage = c;
|
|
+ dev->allocationBlockFinder = blk; // Set it to here to encourage the allocator to
|
|
+ // go forth from here.
|
|
+ //Yaffs2 sanity check:
|
|
+ // This should be the one with the highest sequence number
|
|
+ if(dev->isYaffs2 && (dev->sequenceNumber != bi->sequenceNumber))
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,
|
|
+ (TSTR("yaffs: Allocation block %d was not highest sequence id: block seq = %d, dev seq = %d" TENDSTR),
|
|
+ blk,bi->sequenceNumber,dev->sequenceNumber));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dev->nFreeChunks += (dev->nChunksPerBlock - c);
|
|
+ }
|
|
+ else if(tags.chunkId > 0)
|
|
+ {
|
|
+ // chunkId > 0 so it is a data chunk...
|
|
+ unsigned int endpos;
|
|
+
|
|
+ yaffs_SetChunkBit(dev,blk,c);
|
|
+ bi->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)* dev->nBytesPerChunk + tags.byteCount;
|
|
+ if(in->variantType == YAFFS_OBJECT_TYPE_FILE && in->variant.fileVariant.scannedFileSize <endpos)
|
|
+ {
|
|
+ in->variant.fileVariant.scannedFileSize = endpos;
|
|
+ if(!dev->useHeaderFileSize)
|
|
+ {
|
|
+ in->variant.fileVariant.fileSize = in->variant.fileVariant.scannedFileSize;
|
|
+ }
|
|
+
|
|
+ }
|
|
+ //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
|
|
+ yaffs_SetChunkBit(dev,blk,c);
|
|
+ bi->pagesInUse++;
|
|
+
|
|
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,chunkData,NULL);
|
|
+
|
|
+ oh = (yaffs_ObjectHeader *)chunkData;
|
|
+
|
|
+ in = yaffs_FindObjectByNumber(dev,tags.objectId);
|
|
+ if(in && in->variantType != oh->type)
|
|
+ {
|
|
+ // This should not happen, but somehow
|
|
+ // Wev'e ended up with an objectId that has been reused but not yet
|
|
+ // deleted, and worse still it has changed type. Delete the old object.
|
|
+
|
|
+ yaffs_DestroyObject(in);
|
|
+
|
|
+ in = 0;
|
|
+ }
|
|
+
|
|
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
|
|
+
|
|
+ if(oh->shadowsObject > 0)
|
|
+ {
|
|
+ yaffs_HandleShadowedObject(dev,oh->shadowsObject,0);
|
|
+ }
|
|
+
|
|
+ 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( dev->isYaffs2 ||
|
|
+ ((existingSerial+1) & 3) == newSerial)
|
|
+ {
|
|
+ // Use new one - destroy the exisiting one
|
|
+ yaffs_DeleteChunk(dev,in->chunkId,1,__LINE__);
|
|
+ in->valid = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // Use existing - destroy this one.
|
|
+ yaffs_DeleteChunk(dev,chunk,1,__LINE__);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(!in->valid &&
|
|
+ (tags.objectId == YAFFS_OBJECTID_ROOT ||
|
|
+ tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))
|
|
+ {
|
|
+ // We only load some info, don't fiddle with directory structure
|
|
+ in->valid = 1;
|
|
+ in->variantType = oh->type;
|
|
+
|
|
+ in->yst_mode = oh->yst_mode;
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ in->win_atime[0] = oh->win_atime[0];
|
|
+ in->win_ctime[0] = oh->win_ctime[0];
|
|
+ in->win_mtime[0] = oh->win_mtime[0];
|
|
+ in->win_atime[1] = oh->win_atime[1];
|
|
+ in->win_ctime[1] = oh->win_ctime[1];
|
|
+ in->win_mtime[1] = oh->win_mtime[1];
|
|
+#else
|
|
+ in->yst_uid = oh->yst_uid;
|
|
+ in->yst_gid = oh->yst_gid;
|
|
+ in->yst_atime = oh->yst_atime;
|
|
+ in->yst_mtime = oh->yst_mtime;
|
|
+ in->yst_ctime = oh->yst_ctime;
|
|
+ in->yst_rdev = oh->yst_rdev;
|
|
+#endif
|
|
+ in->chunkId = chunk;
|
|
+
|
|
+ }
|
|
+ else if(!in->valid)
|
|
+ {
|
|
+ // we need to load this info
|
|
+
|
|
+ in->valid = 1;
|
|
+ in->variantType = oh->type;
|
|
+
|
|
+ in->yst_mode = oh->yst_mode;
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ in->win_atime[0] = oh->win_atime[0];
|
|
+ in->win_ctime[0] = oh->win_ctime[0];
|
|
+ in->win_mtime[0] = oh->win_mtime[0];
|
|
+ in->win_atime[1] = oh->win_atime[1];
|
|
+ in->win_ctime[1] = oh->win_ctime[1];
|
|
+ in->win_mtime[1] = oh->win_mtime[1];
|
|
+#else
|
|
+ in->yst_uid = oh->yst_uid;
|
|
+ in->yst_gid = oh->yst_gid;
|
|
+ in->yst_atime = oh->yst_atime;
|
|
+ in->yst_mtime = oh->yst_mtime;
|
|
+ in->yst_ctime = oh->yst_ctime;
|
|
+ in->yst_rdev = oh->yst_rdev;
|
|
+#endif
|
|
+ in->chunkId = chunk;
|
|
+
|
|
+ yaffs_SetObjectName(in,oh->name);
|
|
+ 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
|
|
+
|
|
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." TENDSTR)));
|
|
+ parent = dev->lostNFoundDir;
|
|
+ }
|
|
+
|
|
+ yaffs_AddObjectToDirectory(parent,in);
|
|
+
|
|
+ if(0 && (parent == dev->deletedDir ||
|
|
+ parent == dev->unlinkedDir))
|
|
+ {
|
|
+ in->deleted = 1; // If it is unlinked at start up then it wants deleting
|
|
+ dev->nDeletedFiles++;
|
|
+ }
|
|
+
|
|
+ // 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:
|
|
+ if(dev->isYaffs2 && oh->isShrink)
|
|
+ {
|
|
+ // Prune back the shrunken chunks
|
|
+ yaffs_PruneResizedChunks(in,oh->fileSize);
|
|
+ // Mark the block as having a shrinkHeader
|
|
+ bi->hasShrinkHeader = 1;
|
|
+ }
|
|
+
|
|
+ if(dev->useHeaderFileSize)
|
|
+
|
|
+ in->variant.fileVariant.fileSize = oh->fileSize;
|
|
+
|
|
+ break;
|
|
+ case YAFFS_OBJECT_TYPE_HARDLINK:
|
|
+ in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
|
|
+ in->hardLinks.next = (struct list_head *)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;
|
|
+ }
|
|
+
|
|
+ if(parent == dev->deletedDir)
|
|
+ {
|
|
+ yaffs_DestroyObject(in);
|
|
+ bi->hasShrinkHeader = 1;
|
|
+ }
|
|
+ //T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
|
|
+ {
|
|
+ // If we got this far while scanning, then the block is fully allocated.
|
|
+ state = YAFFS_BLOCK_STATE_FULL;
|
|
+ }
|
|
+
|
|
+ bi->blockState = state;
|
|
+
|
|
+ // Now let's see if it was dirty
|
|
+ if( bi->pagesInUse == 0 &&
|
|
+ !bi->hasShrinkHeader &&
|
|
+ bi->blockState == YAFFS_BLOCK_STATE_FULL)
|
|
+ {
|
|
+ yaffs_BlockBecameDirty(dev,blk);
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ if(blockIndex)
|
|
+ {
|
|
+ YFREE(blockIndex);
|
|
+ }
|
|
+
|
|
+ // Ok, we've done all the scanning.
|
|
+
|
|
+ // 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);
|
|
+
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ // Handle the unlinked files. Since they were left in an unlinked state we should
|
|
+ // just delete them.
|
|
+ {
|
|
+ struct list_head *i;
|
|
+ struct list_head *n;
|
|
+
|
|
+ yaffs_Object *l;
|
|
+ // Soft delete all the unlinked files
|
|
+ list_for_each_safe(i,n,&dev->unlinkedDir->variant.directoryVariant.children)
|
|
+ {
|
|
+ if(i)
|
|
+ {
|
|
+ l = list_entry(i, yaffs_Object,siblings);
|
|
+ yaffs_DestroyObject(l);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ yaffs_ReleaseTempBuffer(dev,chunkData,__LINE__);
|
|
+
|
|
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_Scan ends" TENDSTR)));
|
|
+
|
|
+ return YAFFS_OK;
|
|
+}
|
|
+
|
|
+
|
|
+static int yaffs_ScanBackwards(yaffs_Device *dev)
|
|
+{
|
|
+ yaffs_ExtendedTags tags;
|
|
+ int blk;
|
|
+ int blockIterator;
|
|
+ int startIterator;
|
|
+ int endIterator;
|
|
+ int nBlocksToScan = 0;
|
|
+
|
|
+ int chunk;
|
|
+ int c;
|
|
+ int deleted;
|
|
+ yaffs_BlockState state;
|
|
+ yaffs_Object *hardList = NULL;
|
|
+ yaffs_Object *hl;
|
|
+ yaffs_BlockInfo *bi;
|
|
+ int sequenceNumber;
|
|
+ yaffs_ObjectHeader *oh;
|
|
+ yaffs_Object *in;
|
|
+ yaffs_Object *parent;
|
|
+ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
|
|
+
|
|
+ __u8 *chunkData;
|
|
+
|
|
+ yaffs_BlockIndex *blockIndex = NULL;
|
|
+
|
|
+
|
|
+ if(!dev->isYaffs2)
|
|
+ {
|
|
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR)));
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards starts intstartblk %d intendblk %d..." TENDSTR),dev->internalStartBlock,dev->internalEndBlock));
|
|
+
|
|
+ chunkData = yaffs_GetTempBuffer(dev,__LINE__);
|
|
+
|
|
+
|
|
+ dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
|
|
+
|
|
+ blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));
|
|
+
|
|
+
|
|
+ // Scan all the blocks to determine their state
|
|
+ for(blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++)
|
|
+ {
|
|
+ bi = yaffs_GetBlockInfo(dev,blk);
|
|
+ yaffs_ClearChunkBits(dev,blk);
|
|
+ bi->pagesInUse = 0;
|
|
+ bi->softDeletions = 0;
|
|
+
|
|
+ yaffs_QueryInitialBlockState(dev,blk,&state,&sequenceNumber);
|
|
+
|
|
+ bi->blockState = state;
|
|
+ bi->sequenceNumber = sequenceNumber;
|
|
+
|
|
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block scanning block %d state %d seq %d" TENDSTR),blk,state,sequenceNumber));
|
|
+
|
|
+ if(state == YAFFS_BLOCK_STATE_DEAD)
|
|
+ {
|
|
+ T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));
|
|
+ }
|
|
+ else if(state == YAFFS_BLOCK_STATE_EMPTY)
|
|
+ {
|
|
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block empty " TENDSTR)));
|
|
+ dev->nErasedBlocks++;
|
|
+ dev->nFreeChunks += dev->nChunksPerBlock;
|
|
+ }
|
|
+ else if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
|
|
+ {
|
|
+
|
|
+ // Determine the highest sequence number
|
|
+ if( dev->isYaffs2 &&
|
|
+ sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
|
|
+ sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER)
|
|
+ {
|
|
+
|
|
+ blockIndex[nBlocksToScan].seq = sequenceNumber;
|
|
+ blockIndex[nBlocksToScan].block = blk;
|
|
+
|
|
+ nBlocksToScan++;
|
|
+
|
|
+ if(sequenceNumber >= dev->sequenceNumber)
|
|
+ {
|
|
+ dev->sequenceNumber = sequenceNumber;
|
|
+ }
|
|
+ }
|
|
+ else if(dev->isYaffs2)
|
|
+ {
|
|
+ // TODO: Nasty sequence number!
|
|
+ T(YAFFS_TRACE_SCAN,(TSTR("Block scanning block %d has bad sequence number %d" TENDSTR),blk,sequenceNumber));
|
|
+
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Sort the blocks
|
|
+ // Dungy old bubble sort for now...
|
|
+ {
|
|
+ yaffs_BlockIndex temp;
|
|
+ int i;
|
|
+ int j;
|
|
+
|
|
+ for(i = 0; i < nBlocksToScan; i++)
|
|
+ for(j = i+1; j < nBlocksToScan; j++)
|
|
+ if(blockIndex[i].seq > blockIndex[j].seq)
|
|
+ {
|
|
+ temp = blockIndex[j];
|
|
+ blockIndex[j] = blockIndex[i];
|
|
+ blockIndex[i] = temp;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ // Now scan the blocks looking at the data.
|
|
+ startIterator = 0;
|
|
+ endIterator = nBlocksToScan-1;
|
|
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("%d blocks to be scanned" TENDSTR),nBlocksToScan));
|
|
+
|
|
+
|
|
+ // For each block.... backwards
|
|
+ for(blockIterator = endIterator; blockIterator >= startIterator; blockIterator--)
|
|
+ {
|
|
+
|
|
+ // get the block to scan in the correct order
|
|
+ blk = blockIndex[blockIterator].block;
|
|
+
|
|
+
|
|
+ bi = yaffs_GetBlockInfo(dev,blk);
|
|
+ state = bi->blockState;
|
|
+
|
|
+ deleted = 0;
|
|
+
|
|
+ if( 0 && // Disable since this is redundant.
|
|
+ state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
|
|
+ {
|
|
+ // Let's look at the first chunk in the block
|
|
+ chunk = blk * dev->nChunksPerBlock;
|
|
+
|
|
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
|
|
+
|
|
+ // Let's have a good look at this chunk...
|
|
+
|
|
+ if(!tags.chunkUsed)
|
|
+ {
|
|
+ // An unassigned chunk in the block
|
|
+ // This means that either the block is empty or
|
|
+ // this is the one being allocated from
|
|
+
|
|
+ // We're looking at the first chunk in the block so the block is unused
|
|
+ state = YAFFS_BLOCK_STATE_EMPTY;
|
|
+ dev->nErasedBlocks++;
|
|
+ dev->nFreeChunks += dev->nChunksPerBlock;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ // For each chunk in each block that needs scanning....
|
|
+ for(c = dev->nChunksPerBlock-1; c >= 0 &&
|
|
+ (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
|
|
+ state == YAFFS_BLOCK_STATE_ALLOCATING); c--)
|
|
+ {
|
|
+ // Scan backwards...
|
|
+ // Read the tags and decide what to do
|
|
+ chunk = blk * dev->nChunksPerBlock + c;
|
|
+
|
|
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
|
|
+
|
|
+ // Let's have a good look at this chunk...
|
|
+
|
|
+ if(!tags.chunkUsed)
|
|
+ {
|
|
+ // 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)
|
|
+ {
|
|
+ // We're looking at the first chunk in the block so the block is unused
|
|
+ state = YAFFS_BLOCK_STATE_EMPTY;
|
|
+ dev->nErasedBlocks++;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // this is the block being allocated from
|
|
+ if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
|
|
+ {
|
|
+ T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));
|
|
+ }
|
|
+ state = YAFFS_BLOCK_STATE_ALLOCATING;
|
|
+ dev->allocationBlock = blk;
|
|
+ dev->allocationPage = c;
|
|
+ dev->allocationBlockFinder = blk; // Set it to here to encourage the allocator to
|
|
+ // go forth from here.
|
|
+ //Yaffs2 sanity check:
|
|
+ // This should be the one with the highest sequence number
|
|
+ if(dev->isYaffs2 && (dev->sequenceNumber != bi->sequenceNumber))
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,
|
|
+ (TSTR("yaffs: Allocation block %d was not highest sequence id: block seq = %d, dev seq = %d" TENDSTR),
|
|
+ blk,bi->sequenceNumber,dev->sequenceNumber));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dev->nFreeChunks ++;
|
|
+ }
|
|
+ else if(tags.chunkId > 0)
|
|
+ {
|
|
+ // chunkId > 0 so it is a data chunk...
|
|
+ unsigned int endpos;
|
|
+
|
|
+ __u32 chunkBase = (tags.chunkId - 1)* dev->nBytesPerChunk;
|
|
+
|
|
+ yaffs_SetChunkBit(dev,blk,c);
|
|
+ bi->pagesInUse++;
|
|
+
|
|
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
|
|
+ if(in->variantType == YAFFS_OBJECT_TYPE_FILE &&
|
|
+ chunkBase < in->variant.fileVariant.shrinkSize)
|
|
+ {
|
|
+ // This has not been invalidated by a resize
|
|
+ yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,-1);
|
|
+
|
|
+
|
|
+ // File size is calculated by looking at the data chunks if we have not
|
|
+ // seen an object header yet. Stop this practice once we find an object header.
|
|
+ endpos = (tags.chunkId - 1)* dev->nBytesPerChunk + tags.byteCount;
|
|
+ if(!in->valid && // have not got an object header yet
|
|
+ in->variant.fileVariant.scannedFileSize <endpos)
|
|
+ {
|
|
+ in->variant.fileVariant.scannedFileSize = endpos;
|
|
+ in->variant.fileVariant.fileSize = in->variant.fileVariant.scannedFileSize;
|
|
+ }
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // This chunk has been invalidated by a resize, so delete
|
|
+ yaffs_DeleteChunk(dev,chunk,1,__LINE__);
|
|
+
|
|
+
|
|
+ }
|
|
+ //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
|
|
+ yaffs_SetChunkBit(dev,blk,c);
|
|
+ bi->pagesInUse++;
|
|
+
|
|
+ oh = NULL;
|
|
+ in = NULL;
|
|
+
|
|
+ if(tags.extraHeaderInfoAvailable)
|
|
+ {
|
|
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,tags.extraObjectType);
|
|
+ }
|
|
+
|
|
+
|
|
+ if(!in || !in->valid)
|
|
+ {
|
|
+
|
|
+ // If we don't have valid info then we need to read the chunk
|
|
+ // TODO In future we can probably defer reading the chunk and
|
|
+ // living with invalid data until needed.
|
|
+
|
|
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,chunkData,NULL);
|
|
+
|
|
+ oh = (yaffs_ObjectHeader *)chunkData;
|
|
+
|
|
+ if(!in)
|
|
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
|
|
+
|
|
+ }
|
|
+
|
|
+ if(!in)
|
|
+ {
|
|
+ // TODO Hoosterman we have a problem!
|
|
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: Could not make object for object %d at chunk %d during scan" TENDSTR),tags.objectId,chunk));
|
|
+
|
|
+ }
|
|
+
|
|
+ if(in->valid)
|
|
+ {
|
|
+ // We have already filled this one. We have a duplicate that will be discarded, but
|
|
+ // we first have to suck out resize info if it is a file.
|
|
+
|
|
+ if( (in->variantType == YAFFS_OBJECT_TYPE_FILE) &&
|
|
+ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) ||
|
|
+ (tags.extraHeaderInfoAvailable && tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE))
|
|
+ )
|
|
+ {
|
|
+ __u32 thisSize = (oh) ? oh->fileSize : tags.extraFileLength;
|
|
+ __u32 parentObjectId = (oh) ? oh->parentObjectId : tags.extraParentObjectId;
|
|
+ unsigned isShrink = (oh) ? oh->isShrink : tags.extraIsShrinkHeader;
|
|
+
|
|
+ // If it is deleted (unlinked at start also means deleted)
|
|
+ // we treat the file size as being zeroed at this point.
|
|
+ if(parentObjectId == YAFFS_OBJECTID_DELETED ||
|
|
+ parentObjectId == YAFFS_OBJECTID_UNLINKED)
|
|
+ {
|
|
+ thisSize = 0;
|
|
+ isShrink = 1;
|
|
+ }
|
|
+
|
|
+ if(isShrink &&
|
|
+ in->variant.fileVariant.shrinkSize > thisSize)
|
|
+ {
|
|
+ in->variant.fileVariant.shrinkSize = thisSize;
|
|
+ }
|
|
+
|
|
+ if(isShrink)
|
|
+ {
|
|
+ bi->hasShrinkHeader = 1;
|
|
+ }
|
|
+
|
|
+ }
|
|
+ // Use existing - destroy this one.
|
|
+ yaffs_DeleteChunk(dev,chunk,1,__LINE__);
|
|
+
|
|
+ }
|
|
+
|
|
+ if(!in->valid &&
|
|
+ (tags.objectId == YAFFS_OBJECTID_ROOT ||
|
|
+ tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))
|
|
+ {
|
|
+ // We only load some info, don't fiddle with directory structure
|
|
+ in->valid = 1;
|
|
+ in->variantType = oh->type;
|
|
+
|
|
+ in->yst_mode = oh->yst_mode;
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ in->win_atime[0] = oh->win_atime[0];
|
|
+ in->win_ctime[0] = oh->win_ctime[0];
|
|
+ in->win_mtime[0] = oh->win_mtime[0];
|
|
+ in->win_atime[1] = oh->win_atime[1];
|
|
+ in->win_ctime[1] = oh->win_ctime[1];
|
|
+ in->win_mtime[1] = oh->win_mtime[1];
|
|
+#else
|
|
+ in->yst_uid = oh->yst_uid;
|
|
+ in->yst_gid = oh->yst_gid;
|
|
+ in->yst_atime = oh->yst_atime;
|
|
+ in->yst_mtime = oh->yst_mtime;
|
|
+ in->yst_ctime = oh->yst_ctime;
|
|
+ in->yst_rdev = oh->yst_rdev;
|
|
+#endif
|
|
+ in->chunkId = chunk;
|
|
+
|
|
+ }
|
|
+ else if(!in->valid)
|
|
+ {
|
|
+ // we need to load this info
|
|
+
|
|
+ in->valid = 1;
|
|
+ in->variantType = oh->type;
|
|
+
|
|
+ in->yst_mode = oh->yst_mode;
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ in->win_atime[0] = oh->win_atime[0];
|
|
+ in->win_ctime[0] = oh->win_ctime[0];
|
|
+ in->win_mtime[0] = oh->win_mtime[0];
|
|
+ in->win_atime[1] = oh->win_atime[1];
|
|
+ in->win_ctime[1] = oh->win_ctime[1];
|
|
+ in->win_mtime[1] = oh->win_mtime[1];
|
|
+#else
|
|
+ in->yst_uid = oh->yst_uid;
|
|
+ in->yst_gid = oh->yst_gid;
|
|
+ in->yst_atime = oh->yst_atime;
|
|
+ in->yst_mtime = oh->yst_mtime;
|
|
+ in->yst_ctime = oh->yst_ctime;
|
|
+ in->yst_rdev = oh->yst_rdev;
|
|
+#endif
|
|
+ in->chunkId = chunk;
|
|
+
|
|
+ if(oh->shadowsObject > 0)
|
|
+ {
|
|
+ yaffs_HandleShadowedObject(dev,oh->shadowsObject,0);
|
|
+ }
|
|
+
|
|
+
|
|
+ yaffs_SetObjectName(in,oh->name);
|
|
+ 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
|
|
+
|
|
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." TENDSTR)));
|
|
+ parent = dev->lostNFoundDir;
|
|
+ }
|
|
+
|
|
+ yaffs_AddObjectToDirectory(parent,in);
|
|
+
|
|
+ if((parent == dev->deletedDir ||
|
|
+ parent == dev->unlinkedDir))
|
|
+ {
|
|
+ in->deleted = 1; // If it is unlinked at start up then it wants deleting
|
|
+ }
|
|
+
|
|
+ if( oh->isShrink)
|
|
+ {
|
|
+ // Mark the block as having a shrinkHeader
|
|
+ bi->hasShrinkHeader = 1;
|
|
+ }
|
|
+
|
|
+
|
|
+ // 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:
|
|
+
|
|
+
|
|
+ if(in->variant.fileVariant.scannedFileSize < oh->fileSize)
|
|
+ {
|
|
+ in->variant.fileVariant.fileSize = oh->fileSize;
|
|
+ in->variant.fileVariant.scannedFileSize = in->variant.fileVariant.fileSize;
|
|
+ }
|
|
+
|
|
+ if(oh->isShrink &&
|
|
+ in->variant.fileVariant.shrinkSize > oh->fileSize)
|
|
+ {
|
|
+ in->variant.fileVariant.shrinkSize = oh->fileSize;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ case YAFFS_OBJECT_TYPE_HARDLINK:
|
|
+ in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
|
|
+ in->hardLinks.next = (struct list_head *)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;
|
|
+ }
|
|
+
|
|
+#if 0
|
|
+ if(parent == dev->deletedDir)
|
|
+ {
|
|
+ yaffs_DestroyObject(in);
|
|
+ bi->hasShrinkHeader = 1;
|
|
+ }
|
|
+#endif
|
|
+ //T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
|
|
+ {
|
|
+ // If we got this far while scanning, then the block is fully allocated.
|
|
+ state = YAFFS_BLOCK_STATE_FULL;
|
|
+ }
|
|
+
|
|
+ bi->blockState = state;
|
|
+
|
|
+ // Now let's see if it was dirty
|
|
+ if( bi->pagesInUse == 0 &&
|
|
+ !bi->hasShrinkHeader &&
|
|
+ bi->blockState == YAFFS_BLOCK_STATE_FULL)
|
|
+ {
|
|
+ yaffs_BlockBecameDirty(dev,blk);
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ if(blockIndex)
|
|
+ {
|
|
+ YFREE(blockIndex);
|
|
+ }
|
|
+
|
|
+ // Ok, we've done all the scanning.
|
|
+
|
|
+ // 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);
|
|
+
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ {
|
|
+ struct list_head *i;
|
|
+ struct list_head *n;
|
|
+
|
|
+ yaffs_Object *l;
|
|
+
|
|
+ // Soft delete all the unlinked files
|
|
+ list_for_each_safe(i,n,&dev->unlinkedDir->variant.directoryVariant.children)
|
|
+ {
|
|
+ if(i)
|
|
+ {
|
|
+ l = list_entry(i, yaffs_Object,siblings);
|
|
+ yaffs_DestroyObject(l);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Soft delete all the deletedDir files
|
|
+ list_for_each_safe(i,n,&dev->deletedDir->variant.directoryVariant.children)
|
|
+ {
|
|
+ if(i)
|
|
+ {
|
|
+ l = list_entry(i, yaffs_Object,siblings);
|
|
+ yaffs_DestroyObject(l);
|
|
+
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ yaffs_ReleaseTempBuffer(dev,chunkData,__LINE__);
|
|
+
|
|
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards ends" TENDSTR)));
|
|
+
|
|
+ return YAFFS_OK;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+////////////////////////// Directory Functions /////////////////////////
|
|
+
|
|
+
|
|
+static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj)
|
|
+{
|
|
+
|
|
+ if(!directory)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: Trying to add an object to a null pointer directory" TENDSTR)));
|
|
+ YBUG();
|
|
+ }
|
|
+ if(directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: Trying to add an object to a non-directory" TENDSTR)));
|
|
+ YBUG();
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+
|
|
+ if(directory == obj->myDev->unlinkedDir || directory == obj->myDev->deletedDir)
|
|
+ {
|
|
+ obj->unlinked = 1;
|
|
+ obj->myDev->nUnlinkedFiles++;
|
|
+ obj->renameAllowed = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)
|
|
+{
|
|
+ list_del_init(&obj->siblings);
|
|
+ obj->parent = NULL;
|
|
+}
|
|
+
|
|
+yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const YCHAR *name)
|
|
+{
|
|
+ int sum;
|
|
+
|
|
+ struct list_head *i;
|
|
+ YCHAR buffer[YAFFS_MAX_NAME_LENGTH+1];
|
|
+
|
|
+ yaffs_Object *l;
|
|
+
|
|
+ if(!name)
|
|
+ {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if(!directory)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: yaffs_FindObjectByName: null pointer directory"TENDSTR)));
|
|
+ YBUG();
|
|
+ }
|
|
+ if(directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: yaffs_FindObjectByName: non-directory"TENDSTR)));
|
|
+ YBUG();
|
|
+ }
|
|
+
|
|
+ sum = yaffs_CalcNameSum(name);
|
|
+
|
|
+ list_for_each(i,&directory->variant.directoryVariant.children)
|
|
+ {
|
|
+ if(i)
|
|
+ {
|
|
+ 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)||
|
|
+ l->chunkId <= 0) //LostnFound cunk called Objxxx
|
|
+ {
|
|
+ // 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;
|
|
+
|
|
+
|
|
+ if(!theDir)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: yaffs_FindObjectByName: null pointer directory"TENDSTR)));
|
|
+ YBUG();
|
|
+ }
|
|
+ if(theDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: yaffs_FindObjectByName: non-directory"TENDSTR)));
|
|
+ YBUG();
|
|
+ }
|
|
+
|
|
+ list_for_each(i,&theDir->variant.directoryVariant.children)
|
|
+ {
|
|
+ if(i)
|
|
+ {
|
|
+ l = list_entry(i, yaffs_Object,siblings);
|
|
+ if(l && !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,YCHAR *name,int buffSize)
|
|
+{
|
|
+ memset(name,0,buffSize * sizeof(YCHAR));
|
|
+
|
|
+ if(obj->objectId == YAFFS_OBJECTID_LOSTNFOUND)
|
|
+ {
|
|
+ yaffs_strncpy(name,YAFFS_LOSTNFOUND_NAME,buffSize - 1);
|
|
+ }
|
|
+ else if(obj->chunkId <= 0)
|
|
+ {
|
|
+ YCHAR locName[20];
|
|
+ // make up a name
|
|
+ yaffs_sprintf(locName,_Y("%s%d"),YAFFS_LOSTNFOUND_PREFIX,obj->objectId);
|
|
+ yaffs_strncpy(name,locName,buffSize - 1);
|
|
+
|
|
+ }
|
|
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
|
|
+ else if(obj->shortName[0])
|
|
+ {
|
|
+ yaffs_strcpy(name,obj->shortName);
|
|
+ }
|
|
+#endif
|
|
+ else
|
|
+ {
|
|
+ __u8 *buffer = yaffs_GetTempBuffer(obj->myDev,__LINE__);
|
|
+
|
|
+ yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
|
|
+
|
|
+ memset(buffer,0,obj->myDev->nBytesPerChunk);
|
|
+
|
|
+ if(obj->chunkId >= 0)
|
|
+ {
|
|
+ yaffs_ReadChunkWithTagsFromNAND(obj->myDev,obj->chunkId,buffer,NULL);
|
|
+ }
|
|
+ yaffs_strncpy(name,oh->name,buffSize - 1);
|
|
+
|
|
+ yaffs_ReleaseTempBuffer(obj->myDev,buffer,__LINE__);
|
|
+ }
|
|
+
|
|
+ return yaffs_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 yaffs_strlen(obj->variant.symLinkVariant.alias);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // Only a directory should drop through to here
|
|
+ return obj->myDev->nBytesPerChunk;
|
|
+ }
|
|
+}
|
|
+
|
|
+int yaffs_GetObjectLinkCount(yaffs_Object *obj)
|
|
+{
|
|
+ int count = 0;
|
|
+ struct list_head *i;
|
|
+
|
|
+ if(!obj->unlinked)
|
|
+ {
|
|
+ count++; // the object itself
|
|
+ }
|
|
+ list_for_each(i,&obj->hardLinks)
|
|
+ {
|
|
+ count++; // add the hard links;
|
|
+ }
|
|
+ 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->yst_mode)) return DT_FIFO;
|
|
+ if(S_ISCHR(obj->yst_mode)) return DT_CHR;
|
|
+ if(S_ISBLK(obj->yst_mode)) return DT_BLK;
|
|
+ if(S_ISSOCK(obj->yst_mode)) return DT_SOCK;
|
|
+ default: return DT_REG; break;
|
|
+ }
|
|
+}
|
|
+
|
|
+YCHAR *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(_Y(""));
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifndef CONFIG_YAFFS_WINCE
|
|
+
|
|
+int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)
|
|
+{
|
|
+ unsigned int valid = attr->ia_valid;
|
|
+
|
|
+ if(valid & ATTR_MODE) obj->yst_mode = attr->ia_mode;
|
|
+ if(valid & ATTR_UID) obj->yst_uid = attr->ia_uid;
|
|
+ if(valid & ATTR_GID) obj->yst_gid = attr->ia_gid;
|
|
+
|
|
+ if(valid & ATTR_ATIME) obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime);
|
|
+ if(valid & ATTR_CTIME) obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime);
|
|
+ if(valid & ATTR_MTIME) obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime);
|
|
+
|
|
+ if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);
|
|
+
|
|
+ yaffs_UpdateObjectHeader(obj,NULL,1,0,0);
|
|
+
|
|
+ return YAFFS_OK;
|
|
+
|
|
+}
|
|
+int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)
|
|
+{
|
|
+ unsigned int valid = 0;
|
|
+
|
|
+ attr->ia_mode = obj->yst_mode; valid |= ATTR_MODE;
|
|
+ attr->ia_uid = obj->yst_uid; valid |= ATTR_UID;
|
|
+ attr->ia_gid = obj->yst_gid; valid |= ATTR_GID;
|
|
+
|
|
+ Y_TIME_CONVERT(attr->ia_atime)= obj->yst_atime; valid |= ATTR_ATIME;
|
|
+ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; valid |= ATTR_CTIME;
|
|
+ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; valid |= ATTR_MTIME;
|
|
+
|
|
+ attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;
|
|
+
|
|
+ attr->ia_valid = valid;
|
|
+
|
|
+ return YAFFS_OK;
|
|
+
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+int yaffs_DumpObject(yaffs_Object *obj)
|
|
+{
|
|
+// __u8 buffer[YAFFS_BYTES_PER_CHUNK];
|
|
+ YCHAR 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);
|
|
+
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n" TENDSTR),
|
|
+ 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 ///////////////////////////
|
|
+
|
|
+
|
|
+static int yaffs_CheckDevFunctions(const yaffs_Device *dev)
|
|
+{
|
|
+
|
|
+ // Common functions, gotta have
|
|
+ if(!dev->eraseBlockInNAND ||
|
|
+ !dev->initialiseNAND) return 0;
|
|
+
|
|
+#ifdef CONFIG_YAFFS_YAFFS2
|
|
+
|
|
+ // Can use the "with tags" style interface for yaffs1 or yaffs2
|
|
+ if(dev->writeChunkWithTagsToNAND &&
|
|
+ dev->readChunkWithTagsFromNAND &&
|
|
+ !dev->writeChunkToNAND &&
|
|
+ !dev->readChunkFromNAND &&
|
|
+ dev->markNANDBlockBad &&
|
|
+ dev->queryNANDBlock) return 1;
|
|
+#endif
|
|
+
|
|
+ // Can use the "spare" style interface for yaffs1
|
|
+ if(!dev->isYaffs2 &&
|
|
+ !dev->writeChunkWithTagsToNAND &&
|
|
+ !dev->readChunkWithTagsFromNAND &&
|
|
+ dev->writeChunkToNAND &&
|
|
+ dev->readChunkFromNAND &&
|
|
+ !dev->markNANDBlockBad &&
|
|
+ !dev->queryNANDBlock) return 1;
|
|
+
|
|
+ return 0; // bad
|
|
+}
|
|
+
|
|
+
|
|
+int yaffs_GutsInitialise(yaffs_Device *dev)
|
|
+{
|
|
+ unsigned x;
|
|
+ int bits;
|
|
+ int extraBits;
|
|
+ int nBlocks;
|
|
+
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: yaffs_GutsInitialise()" TENDSTR)));
|
|
+ // Check stuff that must be set
|
|
+
|
|
+ if(!dev)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Need a device" TENDSTR)));
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ dev->internalStartBlock = dev->startBlock;
|
|
+ dev->internalEndBlock = dev->endBlock;
|
|
+ dev->blockOffset = 0;
|
|
+ dev->chunkOffset = 0;
|
|
+ dev->nFreeChunks = 0;
|
|
+
|
|
+ if(dev->startBlock == 0)
|
|
+ {
|
|
+ dev->internalStartBlock = dev->startBlock + 1;
|
|
+ dev->internalEndBlock = dev->endBlock + 1;
|
|
+ dev->blockOffset = 1;
|
|
+ dev->chunkOffset = dev->nChunksPerBlock;
|
|
+ }
|
|
+
|
|
+ // Check geometry parameters.
|
|
+
|
|
+ if( (dev->isYaffs2 && dev->nBytesPerChunk <1024) ||
|
|
+ (!dev->isYaffs2 && dev->nBytesPerChunk !=512) ||
|
|
+ dev->nChunksPerBlock < 2 ||
|
|
+ dev->nReservedBlocks < 2 ||
|
|
+ dev->internalStartBlock <= 0 ||
|
|
+ dev->internalEndBlock <= 0 ||
|
|
+ dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2) // otherwise it is too small
|
|
+ )
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s " TENDSTR),
|
|
+ dev->nBytesPerChunk, dev->isYaffs2 ? "2" : ""));
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ if(yaffs_InitialiseNAND(dev) != YAFFS_OK)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: InitialiseNAND failed" TENDSTR)));
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ // Got the right mix of functions?
|
|
+ //
|
|
+ if(!yaffs_CheckDevFunctions(dev))
|
|
+ {
|
|
+ //Function missing
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: device function(s) missing or wrong\n" TENDSTR)));
|
|
+
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ // This is really a compilation check.
|
|
+ if(!yaffs_CheckStructures())
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs_CheckStructures failed\n" TENDSTR)));
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ if(dev->isMounted)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: device already mounted\n" TENDSTR)));
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ //
|
|
+ //
|
|
+ // Finished with most checks. One or two more checks happen later on too.
|
|
+ //
|
|
+
|
|
+ dev->isMounted = 1;
|
|
+
|
|
+
|
|
+ nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
|
|
+
|
|
+
|
|
+
|
|
+ // OK now calculate a few things for the device
|
|
+ // Calculate chunkGroupBits.
|
|
+ // We need to find the next power of 2 > than internalEndBlock
|
|
+
|
|
+ x = dev->nChunksPerBlock * (dev->internalEndBlock+1);
|
|
+
|
|
+ for(bits = extraBits = 0; x > 1; bits++)
|
|
+ {
|
|
+ if(x & 1) extraBits++;
|
|
+ x >>= 1;
|
|
+ }
|
|
+
|
|
+ if(extraBits > 0) bits++;
|
|
+
|
|
+
|
|
+ // Level0 Tnodes are 16 bits, so if the bitwidth of the
|
|
+ // chunk range we're using is greater than 16 we need
|
|
+ // to figure out chunk shift and chunkGroupSize
|
|
+ if(bits <= 16)
|
|
+ {
|
|
+ dev->chunkGroupBits = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ dev->chunkGroupBits = bits - 16;
|
|
+ }
|
|
+
|
|
+ dev->chunkGroupSize = 1 << dev->chunkGroupBits;
|
|
+
|
|
+ if(dev->nChunksPerBlock < dev->chunkGroupSize)
|
|
+ {
|
|
+ // We have a problem because the soft delete won't work if
|
|
+ // the chunk group size > chunks per block.
|
|
+ // This can be remedied by using larger "virtual blocks".
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: chunk group too large\n" TENDSTR)));
|
|
+
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+
|
|
+ // OK, we've finished verifying the device, lets continue with initialisation
|
|
+
|
|
+ // More device initialisation
|
|
+ dev->garbageCollections = 0;
|
|
+ dev->passiveGarbageCollections = 0;
|
|
+ dev->currentDirtyChecker = 0;
|
|
+ dev->bufferedBlock = -1;
|
|
+ dev->doingBufferedBlockRewrite = 0;
|
|
+ dev->nDeletedFiles = 0;
|
|
+ dev->nBackgroundDeletions=0;
|
|
+ dev->nUnlinkedFiles = 0;
|
|
+ dev->eccFixed=0;
|
|
+ dev->eccUnfixed=0;
|
|
+ dev->tagsEccFixed=0;
|
|
+ dev->tagsEccUnfixed=0;
|
|
+ dev->nErasureFailures = 0;
|
|
+ dev->nErasedBlocks = 0;
|
|
+ dev->isDoingGC = 0;
|
|
+
|
|
+ //dev->localBuffer = YMALLOC(dev->nBytesPerChunk);
|
|
+ // Initialise temporary buffers
|
|
+ {
|
|
+ int i;
|
|
+ for(i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
|
|
+ {
|
|
+ dev->tempBuffer[i].line = 0; // not in use
|
|
+ dev->tempBuffer[i].buffer = YMALLOC(dev->nBytesPerChunk);
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+
|
|
+ yaffs_InitialiseBlocks(dev,nBlocks);
|
|
+
|
|
+ yaffs_InitialiseTnodes(dev);
|
|
+
|
|
+ yaffs_InitialiseObjects(dev);
|
|
+
|
|
+ dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32));
|
|
+
|
|
+ if(dev->nShortOpCaches > 0)
|
|
+ {
|
|
+ int i;
|
|
+
|
|
+ if(dev->nShortOpCaches > YAFFS_MAX_SHORT_OP_CACHES)
|
|
+ {
|
|
+ dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES;
|
|
+ }
|
|
+
|
|
+ dev->srCache = YMALLOC( dev->nShortOpCaches * sizeof(yaffs_ChunkCache));
|
|
+
|
|
+ for(i=0; i < dev->nShortOpCaches; i++)
|
|
+ {
|
|
+ dev->srCache[i].object = NULL;
|
|
+ dev->srCache[i].lastUse = 0;
|
|
+ dev->srCache[i].dirty = 0;
|
|
+ dev->srCache[i].data = YMALLOC(dev->nBytesPerChunk);
|
|
+ }
|
|
+ dev->srLastUse = 0;
|
|
+ }
|
|
+
|
|
+ dev->cacheHits = 0;
|
|
+
|
|
+
|
|
+ // Initialise the unlinked, root and lost and found directories
|
|
+ dev->lostNFoundDir = dev->rootDir = dev->unlinkedDir = dev->deletedDir = NULL;
|
|
+
|
|
+ dev->unlinkedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_UNLINKED, S_IFDIR);
|
|
+ dev->deletedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_DELETED, S_IFDIR);
|
|
+
|
|
+ dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR);
|
|
+ dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_LOSTNFOUND_MODE | S_IFDIR);
|
|
+ yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir);
|
|
+
|
|
+ if(dev->isYaffs2)
|
|
+ {
|
|
+ dev->useHeaderFileSize = 1;
|
|
+ }
|
|
+
|
|
+ // Now scan the flash.
|
|
+
|
|
+ if(dev->isYaffs2)
|
|
+ yaffs_ScanBackwards(dev);
|
|
+ else
|
|
+ yaffs_Scan(dev);
|
|
+
|
|
+ // Zero out stats
|
|
+ dev->nPageReads = 0;
|
|
+ dev->nPageWrites = 0;
|
|
+ dev->nBlockErasures = 0;
|
|
+ dev->nGCCopies = 0;
|
|
+ dev->nRetriedWrites = 0;
|
|
+
|
|
+ dev->nRetiredBlocks = 0;
|
|
+
|
|
+ yaffs_VerifyFreeChunks(dev);
|
|
+
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR)));
|
|
+ return YAFFS_OK;
|
|
+
|
|
+}
|
|
+
|
|
+void yaffs_Deinitialise(yaffs_Device *dev)
|
|
+{
|
|
+ if(dev->isMounted)
|
|
+ {
|
|
+ int i;
|
|
+
|
|
+ yaffs_DeinitialiseBlocks(dev);
|
|
+ yaffs_DeinitialiseTnodes(dev);
|
|
+ yaffs_DeinitialiseObjects(dev);
|
|
+ if(dev->nShortOpCaches > 0)
|
|
+ {
|
|
+
|
|
+ for(i=0; i < dev->nShortOpCaches; i++)
|
|
+ {
|
|
+ YFREE(dev->srCache[i].data);
|
|
+ }
|
|
+
|
|
+ YFREE(dev->srCache);
|
|
+ }
|
|
+
|
|
+ YFREE(dev->gcCleanupList);
|
|
+
|
|
+ for(i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
|
|
+ {
|
|
+ YFREE(dev->tempBuffer[i].buffer);
|
|
+ }
|
|
+
|
|
+ dev->isMounted = 0;
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+#if 0
|
|
+
|
|
+int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
|
|
+{
|
|
+ int nFree = dev->nFreeChunks - (dev->nChunksPerBlock * YAFFS_RESERVED_BLOCKS);
|
|
+
|
|
+ struct list_head *i;
|
|
+ yaffs_Object *l;
|
|
+
|
|
+
|
|
+ // To the free chunks add the chunks that are in the deleted unlinked files.
|
|
+ list_for_each(i,&dev->deletedDir->variant.directoryVariant.children)
|
|
+ {
|
|
+ l = list_entry(i, yaffs_Object,siblings);
|
|
+ if(l->deleted)
|
|
+ {
|
|
+ nFree++;
|
|
+ nFree += l->nDataChunks;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ // printf("___________ nFreeChunks is %d nFree is %d\n",dev->nFreeChunks,nFree);
|
|
+
|
|
+ if(nFree < 0) nFree = 0;
|
|
+
|
|
+ return nFree;
|
|
+
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+static int yaffs_CountFreeChunks(yaffs_Device *dev)
|
|
+{
|
|
+ int nFree;
|
|
+ int b;
|
|
+
|
|
+ yaffs_BlockInfo *blk;
|
|
+
|
|
+
|
|
+ for(nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock; b++)
|
|
+ {
|
|
+ blk = yaffs_GetBlockInfo(dev,b);
|
|
+
|
|
+ switch(blk->blockState)
|
|
+ {
|
|
+ case YAFFS_BLOCK_STATE_EMPTY:
|
|
+ case YAFFS_BLOCK_STATE_ALLOCATING:
|
|
+ case YAFFS_BLOCK_STATE_COLLECTING:
|
|
+ case YAFFS_BLOCK_STATE_FULL: nFree += (dev->nChunksPerBlock - blk->pagesInUse + blk->softDeletions); break;
|
|
+ default: break;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ return nFree;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
|
|
+{
|
|
+ // This is what we report to the outside world
|
|
+
|
|
+ int nFree;
|
|
+ int nDirtyCacheChunks;
|
|
+
|
|
+#if 1
|
|
+ nFree = dev->nFreeChunks;
|
|
+#else
|
|
+ nFree = yaffs_CountFreeChunks(dev);
|
|
+#endif
|
|
+
|
|
+ // Now count the number of dirty chunks in the cache and subtract those
|
|
+
|
|
+ {
|
|
+ int i;
|
|
+ for( nDirtyCacheChunks = 0,i = 0; i < dev->nShortOpCaches; i++)
|
|
+ {
|
|
+ if(dev->srCache[i].dirty) nDirtyCacheChunks++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nFree -= nDirtyCacheChunks;
|
|
+
|
|
+ nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock);
|
|
+
|
|
+ if(nFree < 0) nFree = 0;
|
|
+
|
|
+ return nFree;
|
|
+
|
|
+}
|
|
+
|
|
+static int yaffs_freeVerificationFailures;
|
|
+
|
|
+static void yaffs_VerifyFreeChunks(yaffs_Device *dev)
|
|
+{
|
|
+ int counted = yaffs_CountFreeChunks(dev);
|
|
+
|
|
+ int difference = dev->nFreeChunks - counted;
|
|
+
|
|
+ if(difference)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("Freechunks verification failure %d %d %d" TENDSTR),dev->nFreeChunks,counted,difference));
|
|
+ yaffs_freeVerificationFailures++;
|
|
+ }
|
|
+}
|
|
+
|
|
+/////////////////// YAFFS test code //////////////////////////////////
|
|
+
|
|
+#define yaffs_CheckStruct(structure,syze, name) \
|
|
+ if(sizeof(structure) != syze) \
|
|
+ { \
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),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")
|
|
+#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG
|
|
+ yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode")
|
|
+#endif
|
|
+ yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader")
|
|
+
|
|
+
|
|
+ return YAFFS_OK;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+void yaffs_GutsTest(yaffs_Device *dev)
|
|
+{
|
|
+
|
|
+ if(yaffs_CheckStructures() != YAFFS_OK)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ALWAYS,(TSTR("One or more structures malformed-- aborting\n" TENDSTR)));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ yaffs_TnodeTest(dev);
|
|
+ yaffs_ObjectTest(dev);
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_guts.h b/fs/yaffs/yaffs_guts.h
|
|
new file mode 100644
|
|
index 0000000..58c52e0
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_guts.h
|
|
@@ -0,0 +1,759 @@
|
|
+/*
|
|
+ * 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 <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ *
|
|
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
+ *
|
|
+ * $Id: yaffs_guts.h,v 1.11 2005/07/31 06:52:40 charles Exp $
|
|
+ */
|
|
+
|
|
+#ifndef __YAFFS_GUTS_H__
|
|
+#define __YAFFS_GUTS_H__
|
|
+
|
|
+#include "devextras.h"
|
|
+#include "yportenv.h"
|
|
+
|
|
+#define YAFFS_OK 1
|
|
+#define YAFFS_FAIL 0
|
|
+
|
|
+// Give us a Y=0x59,
|
|
+// Give us an A=0x41,
|
|
+// Give us an FF=0xFF
|
|
+// Give us an S=0x53
|
|
+// And what have we got...
|
|
+#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
|
|
+
|
|
+#ifndef CONFIG_YAFFS_NO_YAFFS1
|
|
+#define YAFFS_BYTES_PER_SPARE 16
|
|
+#define YAFFS_BYTES_PER_CHUNK 512
|
|
+#define YAFFS_CHUNK_SIZE_SHIFT 9
|
|
+#define YAFFS_CHUNKS_PER_BLOCK 32
|
|
+#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
|
|
+#endif
|
|
+
|
|
+#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024
|
|
+#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32
|
|
+
|
|
+#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_OBJECT_SPACE 0x40000
|
|
+
|
|
+#ifdef CONFIG_YAFFS_UNICODE
|
|
+#define YAFFS_MAX_NAME_LENGTH 127
|
|
+#define YAFFS_MAX_ALIAS_LENGTH 79
|
|
+#else
|
|
+#define YAFFS_MAX_NAME_LENGTH 255
|
|
+#define YAFFS_MAX_ALIAS_LENGTH 159
|
|
+#endif
|
|
+
|
|
+#define YAFFS_SHORT_NAME_LENGTH 15
|
|
+
|
|
+
|
|
+#define YAFFS_OBJECTID_ROOT 1
|
|
+#define YAFFS_OBJECTID_LOSTNFOUND 2
|
|
+#define YAFFS_OBJECTID_UNLINKED 3
|
|
+#define YAFFS_OBJECTID_DELETED 4
|
|
+
|
|
+#define YAFFS_MAX_SHORT_OP_CACHES 20
|
|
+
|
|
+#define YAFFS_N_TEMP_BUFFERS 4
|
|
+
|
|
+// Sequence numbers are used in YAFFS2 to determine block allocation order.
|
|
+// The range is limited slightly to help distinguish bad numbers from good.
|
|
+// This also allows us to perhaps in the future use special numbers for
|
|
+// special purposes.
|
|
+// EFFFFF00 allows the allocation of 8 blocks per second (~1Mbytes) for 15 years,
|
|
+// and is a larger number than the lifetime of a 2GB device.
|
|
+
|
|
+#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000
|
|
+#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xEFFFFF00
|
|
+
|
|
+
|
|
+// ChunkCache is used for short read/write operations.
|
|
+typedef struct
|
|
+{
|
|
+ struct yaffs_ObjectStruct *object;
|
|
+ int chunkId;
|
|
+ int lastUse;
|
|
+ int dirty;
|
|
+ int nBytes; // Only valid if the cache is dirty
|
|
+ int locked; // Can't push out or flush while locked..
|
|
+#ifdef CONFIG_YAFFS_YAFFS2
|
|
+ __u8 *data;
|
|
+#else
|
|
+ __u8 data[YAFFS_BYTES_PER_CHUNK];
|
|
+#endif
|
|
+} yaffs_ChunkCache;
|
|
+
|
|
+
|
|
+#ifndef CONFIG_YAFFS_NO_YAFFS1
|
|
+// Tags structures in RAM
|
|
+// NB This uses bitfield. Bitfields should not straddle 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;
|
|
+
|
|
+#endif
|
|
+
|
|
+typedef enum
|
|
+{
|
|
+ YAFFS_ECC_RESULT_UNKNOWN,
|
|
+ YAFFS_ECC_RESULT_NO_ERROR,
|
|
+ YAFFS_ECC_RESULT_FIXED,
|
|
+ YAFFS_ECC_RESULT_UNFIXED
|
|
+} yaffs_ECCResult;
|
|
+
|
|
+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
|
|
+{
|
|
+
|
|
+ unsigned validMarker0;
|
|
+ unsigned chunkUsed; // Status of the chunk: used or unused
|
|
+ unsigned objectId; // If 0 then this is not part of an object (unused)
|
|
+ unsigned chunkId; // If 0 then this is a header, else a data chunk
|
|
+ unsigned byteCount; // Only valid for data chunks
|
|
+
|
|
+
|
|
+ // The following stuff only has meaning when we read
|
|
+ yaffs_ECCResult eccResult; // Only valid when we read.
|
|
+ unsigned blockBad; // Only valid on reading
|
|
+
|
|
+ // YAFFS 1 stuff
|
|
+ unsigned chunkDeleted; // The chunk is marked deleted
|
|
+ unsigned serialNumber; // Yaffs1 2-bit serial number
|
|
+
|
|
+ // YAFFS2 stuff
|
|
+ unsigned sequenceNumber; // The sequence number of this block
|
|
+
|
|
+ // Extra info if this is an object header (YAFFS2 only)
|
|
+
|
|
+ unsigned extraHeaderInfoAvailable; // There is extra info available if this is not zero
|
|
+ unsigned extraParentObjectId; // The parent object
|
|
+ unsigned extraIsShrinkHeader; // Is it a shrink header?
|
|
+ unsigned extraShadows; // Does this shadow another object?
|
|
+
|
|
+ yaffs_ObjectType extraObjectType; // What object type?
|
|
+
|
|
+ unsigned extraFileLength; // Length if it is a file
|
|
+ unsigned extraEquivalentObjectId; // Equivalent object Id if it is a hard link
|
|
+
|
|
+ unsigned validMarker1;
|
|
+
|
|
+} yaffs_ExtendedTags;
|
|
+
|
|
+#ifndef CONFIG_YAFFS_NO_YAFFS1
|
|
+// Spare structure
|
|
+typedef struct
|
|
+{
|
|
+ __u8 tagByte0;
|
|
+ __u8 tagByte1;
|
|
+ __u8 tagByte2;
|
|
+ __u8 tagByte3;
|
|
+ __u8 pageStatus; // set to 0 to delete the chunk
|
|
+ __u8 blockStatus;
|
|
+ __u8 tagByte4;
|
|
+ __u8 tagByte5;
|
|
+ __u8 ecc1[3];
|
|
+ __u8 tagByte6;
|
|
+ __u8 tagByte7;
|
|
+ __u8 ecc2[3];
|
|
+} yaffs_Spare;
|
|
+
|
|
+//Special structure for passing through to mtd
|
|
+struct yaffs_NANDSpare {
|
|
+ yaffs_Spare spare;
|
|
+ int eccres1;
|
|
+ int eccres2;
|
|
+};
|
|
+#endif
|
|
+
|
|
+
|
|
+// Block data in RAM
|
|
+
|
|
+typedef enum {
|
|
+ YAFFS_BLOCK_STATE_UNKNOWN = 0,
|
|
+
|
|
+ YAFFS_BLOCK_STATE_SCANNING,
|
|
+ YAFFS_BLOCK_STATE_NEEDS_SCANNING,// The block might have something on it (ie it is allocating or full, perhaps empty)
|
|
+ // but it needs to be scanned to determine its true state.
|
|
+ // This state is only valid during yaffs_Scan.
|
|
+ // NB We tolerate empty because the pre-scanner might be incapable of deciding
|
|
+ // However, if this state is returned on a YAFFS2 device, then we expect a sequence number
|
|
+
|
|
+ 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_COLLECTING, // This block is being garbage collected
|
|
+
|
|
+ YAFFS_BLOCK_STATE_DEAD // This block has failed and is not in use
|
|
+
|
|
+} yaffs_BlockState;
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+
|
|
+ int softDeletions:12; // number of soft deleted pages
|
|
+ int pagesInUse:12; // number of pages in use
|
|
+ yaffs_BlockState blockState:4; // One of the above block states
|
|
+ __u32 needsRetiring:1; // Data has failed on this block, need to get valid data off
|
|
+ // and retire the block.
|
|
+#ifdef CONFIG_YAFFS_YAFFS2
|
|
+ __u32 hasShrinkHeader:1;// This block has at least one object header that does a shrink
|
|
+ __u32 sequenceNumber; // block sequence number for yaffs2
|
|
+#endif
|
|
+
|
|
+} yaffs_BlockInfo;
|
|
+
|
|
+
|
|
+//////////////////// Object structure ///////////////////////////
|
|
+// This is the object structure as stored on NAND
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ yaffs_ObjectType type;
|
|
+
|
|
+ // Apply to everything
|
|
+ int parentObjectId;
|
|
+ __u16 sum__NoLongerUsed; // checksum of name. Calc this off the name to prevent inconsistencies
|
|
+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
|
|
+
|
|
+ // Thes following apply to directories, files, symlinks - not hard links
|
|
+ __u32 yst_mode; // protection
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ __u32 notForWinCE[5];
|
|
+#else
|
|
+ __u32 yst_uid; // user ID of owner
|
|
+ __u32 yst_gid; // group ID of owner
|
|
+ __u32 yst_atime; // time of last access
|
|
+ __u32 yst_mtime; // time of last modification
|
|
+ __u32 yst_ctime; // time of last change
|
|
+#endif
|
|
+
|
|
+ // File size applies to files only
|
|
+ int fileSize;
|
|
+
|
|
+ // Equivalent object id applies to hard links only.
|
|
+ int equivalentObjectId;
|
|
+
|
|
+ // Alias is for symlinks only.
|
|
+ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1];
|
|
+
|
|
+ __u32 yst_rdev; // device stuff for block and char devices (maj/min)
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ __u32 win_ctime[2];
|
|
+ __u32 win_atime[2];
|
|
+ __u32 win_mtime[2];
|
|
+ __u32 roomToGrow[4];
|
|
+#else
|
|
+ __u32 roomToGrow[10];
|
|
+#endif
|
|
+
|
|
+ int shadowsObject; // This object header shadows the specified object if not > 0
|
|
+
|
|
+ // isShrink applies to object headers written when we shrink the file (ie resize)
|
|
+ __u32 isShrink;
|
|
+
|
|
+} yaffs_ObjectHeader;
|
|
+
|
|
+
|
|
+
|
|
+//////////////////// Tnode ///////////////////////////
|
|
+
|
|
+union yaffs_Tnode_union
|
|
+{
|
|
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
|
|
+ union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL+1];
|
|
+#else
|
|
+ union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL];
|
|
+#endif
|
|
+ __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 shrinkSize;
|
|
+ int topLevel;
|
|
+ yaffs_Tnode *top;
|
|
+} yaffs_FileStructure;
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ struct list_head children; // list of child links
|
|
+} yaffs_DirectoryStructure;
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ YCHAR *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 deleted: 1; // This should only apply to unlinked files.
|
|
+ __u8 softDeleted: 1; // it has also been soft deleted
|
|
+ __u8 unlinked: 1; // An unlinked file. The file should be in the unlinked pseudo directory.
|
|
+ __u8 fake:1; // A fake object has no presence on NAND.
|
|
+ __u8 renameAllowed:1; // Some objects are not allowed to be renamed.
|
|
+ __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
|
|
+
|
|
+ __u8 deferedFree: 1; // For Linux kernel. Object is removed from NAND, but still in the inode cache.
|
|
+ // Free of object is defered.
|
|
+
|
|
+ __u8 lazyLoaded; // Vital info has been loaded from tags. Not all info available.
|
|
+ //
|
|
+
|
|
+ // 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 object header in NAND?
|
|
+ int chunkId; // where it lives
|
|
+
|
|
+ int nDataChunks; // Number of data chunks attached to the file.
|
|
+
|
|
+ __u32 objectId; // the object id value
|
|
+
|
|
+
|
|
+ __u32 yst_mode; // protection
|
|
+
|
|
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
|
|
+ YCHAR shortName[YAFFS_SHORT_NAME_LENGTH+1];
|
|
+#endif
|
|
+
|
|
+#ifndef __KERNEL__
|
|
+ __u32 inUse;
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+ __u32 win_ctime[2];
|
|
+ __u32 win_mtime[2];
|
|
+ __u32 win_atime[2];
|
|
+#else
|
|
+ __u32 yst_uid; // user ID of owner
|
|
+ __u32 yst_gid; // group ID of owner
|
|
+ __u32 yst_atime; // time of last access
|
|
+ __u32 yst_mtime; // time of last modification
|
|
+ __u32 yst_ctime; // time of last change
|
|
+#endif
|
|
+
|
|
+ __u32 yst_rdev; // device stuff for block and char devices
|
|
+
|
|
+
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+ struct inode *myInode;
|
|
+
|
|
+#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;
|
|
+ int count;
|
|
+} yaffs_ObjectBucket;
|
|
+
|
|
+///////////////////// Temporary buffers ////////////////////
|
|
+//
|
|
+// These are chunk-sized working buffers. Each device has a few
|
|
+
|
|
+typedef struct {
|
|
+ __u8 *buffer;
|
|
+ int line; // track from whence this buffer was allocated
|
|
+ int maxLine;
|
|
+} yaffs_TempBuffer;
|
|
+
|
|
+//////////////////// Device ////////////////////////////////
|
|
+
|
|
+struct yaffs_DeviceStruct
|
|
+{
|
|
+ struct list_head devList;
|
|
+ const char *name;
|
|
+
|
|
+ // Entry parameters set up way early. Yaffs sets up the rest.
|
|
+ int nBytesPerChunk; // Should be a power of 2 >= 512
|
|
+ int nChunksPerBlock; // does not need to be a power of 2
|
|
+ int nBytesPerSpare; // spare area size
|
|
+ int startBlock; // Start block we're allowed to use
|
|
+ int endBlock; // End block we're allowed to use
|
|
+ int nReservedBlocks; // We want this tuneable so that we can reduce
|
|
+ // reserved blocks on NOR and RAM.
|
|
+
|
|
+ int nShortOpCaches; // If <= 0, then short op caching is disabled, else
|
|
+ // the number of short op caches (don't use too many).
|
|
+
|
|
+ int useHeaderFileSize; // Flag to determine if we should use file sizes from the header
|
|
+
|
|
+ int useNANDECC; // Flag to decide whether or not to use NANDECC
|
|
+
|
|
+
|
|
+ void *genericDevice; // Pointer to device context
|
|
+ // On an mtd this holds the mtd pointer.
|
|
+
|
|
+ // NAND access functions (Must be set before calling YAFFS)
|
|
+
|
|
+
|
|
+ int (*writeChunkToNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, const 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 CONFIG_YAFFS_YAFFS2
|
|
+ int (*writeChunkWithTagsToNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, const yaffs_ExtendedTags *tags);
|
|
+ int (*readChunkWithTagsFromNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_ExtendedTags *tags);
|
|
+ int (*markNANDBlockBad)(struct yaffs_DeviceStruct *dev, int blockNo);
|
|
+ int (*queryNANDBlock)(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState *state, int *sequenceNumber);
|
|
+#endif
|
|
+
|
|
+ int isYaffs2;
|
|
+
|
|
+ // End of stuff that must be set before initialisation.
|
|
+
|
|
+ // Runtime parameters. Set up by YAFFS.
|
|
+
|
|
+ __u16 chunkGroupBits; // 0 for devices <= 32MB. else log2(nchunks) - 16
|
|
+ __u16 chunkGroupSize; // == 2^^chunkGroupBits
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+
|
|
+ struct semaphore sem;// Semaphore for waiting on erasure.
|
|
+ struct semaphore grossLock; // Gross locking semaphore
|
|
+ __u8 * spareBuffer; // For mtdif2 use. Don't know the size of the buffer at compile time so we have to allocate it.
|
|
+ void (*putSuperFunc)(struct super_block *sb);
|
|
+#endif
|
|
+
|
|
+ int isMounted;
|
|
+
|
|
+ // Stuff to support block offsetting to support start block zero
|
|
+ int internalStartBlock;
|
|
+ int internalEndBlock;
|
|
+ int blockOffset;
|
|
+ int chunkOffset;
|
|
+
|
|
+ // Block Info
|
|
+ yaffs_BlockInfo *blockInfo;
|
|
+ __u8 *chunkBits; // bitmap of chunks in use
|
|
+ int chunkBitmapStride; // Number of bytes of chunkBits per block.
|
|
+ // Must be consistent with nChunksPerBlock.
|
|
+
|
|
+
|
|
+ int nErasedBlocks;
|
|
+ int allocationBlock; // Current block being allocated off
|
|
+ __u32 allocationPage;
|
|
+ int allocationBlockFinder; // Used to search for next allocation block
|
|
+
|
|
+ // Runtime state
|
|
+ int nTnodesCreated;
|
|
+ yaffs_Tnode *freeTnodes;
|
|
+ int nFreeTnodes;
|
|
+ yaffs_TnodeList *allocatedTnodeList;
|
|
+
|
|
+ int isDoingGC;
|
|
+
|
|
+ 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
|
|
+
|
|
+ __u32 *gcCleanupList; // objects to delete at the end of a GC.
|
|
+
|
|
+ // Operations since mount
|
|
+ int nPageWrites;
|
|
+ int nPageReads;
|
|
+ int nBlockErasures;
|
|
+ int nErasureFailures;
|
|
+ int nGCCopies;
|
|
+ int garbageCollections;
|
|
+ int passiveGarbageCollections;
|
|
+ int nRetriedWrites;
|
|
+ int nRetiredBlocks;
|
|
+ int eccFixed;
|
|
+ int eccUnfixed;
|
|
+ int tagsEccFixed;
|
|
+ int tagsEccUnfixed;
|
|
+ int nDeletions;
|
|
+ int nUnmarkedDeletions;
|
|
+
|
|
+ yaffs_Object *rootDir;
|
|
+ yaffs_Object *lostNFoundDir;
|
|
+
|
|
+ // Buffer areas for storing data to recover from write failures
|
|
+// __u8 bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK];
|
|
+// yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK];
|
|
+ int bufferedBlock; // Which block is buffered here?
|
|
+ int doingBufferedBlockRewrite;
|
|
+
|
|
+ yaffs_ChunkCache *srCache;
|
|
+ int srLastUse;
|
|
+
|
|
+ int cacheHits;
|
|
+
|
|
+ // Stuff for background deletion and unlinked files.
|
|
+ yaffs_Object *unlinkedDir; // Directory where unlinked and deleted files live.
|
|
+ yaffs_Object *deletedDir; // Directory where deleted objects are sent to disappear.
|
|
+ yaffs_Object *unlinkedDeletion; // Current file being background deleted.
|
|
+ int nDeletedFiles; // Count of files awaiting deletion;
|
|
+ int nUnlinkedFiles; // Count of unlinked files.
|
|
+ int nBackgroundDeletions; // Count of background deletions.
|
|
+
|
|
+ //__u8 *localBuffer;
|
|
+
|
|
+ yaffs_TempBuffer tempBuffer[YAFFS_N_TEMP_BUFFERS];
|
|
+ int maxTemp;
|
|
+ int unmanagedTempAllocations;
|
|
+ int unmanagedTempDeallocations;
|
|
+
|
|
+ // yaffs2 runtime stuff
|
|
+ unsigned sequenceNumber; //Sequence number of currently allocating block
|
|
+ unsigned oldestDirtySequence;
|
|
+
|
|
+};
|
|
+
|
|
+typedef struct yaffs_DeviceStruct yaffs_Device;
|
|
+
|
|
+
|
|
+// Function to manipulate block info
|
|
+static Y_INLINE yaffs_BlockInfo* yaffs_GetBlockInfo(yaffs_Device *dev, int blk)
|
|
+{
|
|
+ if(blk < dev->internalStartBlock || blk > dev->internalEndBlock)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR),blk));
|
|
+ YBUG();
|
|
+ }
|
|
+ return &dev->blockInfo[blk - dev->internalStartBlock];
|
|
+}
|
|
+
|
|
+
|
|
+//////////// 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 YCHAR *oldName, yaffs_Object *newDir, const YCHAR *newName);
|
|
+
|
|
+// generic Object functions
|
|
+int yaffs_Unlink(yaffs_Object *dir, const YCHAR *name);
|
|
+int yaffs_DeleteFile(yaffs_Object *obj);
|
|
+
|
|
+// Object access functions.
|
|
+int yaffs_GetObjectName(yaffs_Object *obj,YCHAR *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 writeThrough);
|
|
+int yaffs_ResizeFile(yaffs_Object *obj, int newSize);
|
|
+
|
|
+yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const YCHAR *name, __u32 mode, __u32 uid, __u32 gid);
|
|
+int yaffs_FlushFile(yaffs_Object *obj,int updateTime);
|
|
+
|
|
+
|
|
+// Directory operations
|
|
+yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const YCHAR *name, __u32 mode, __u32 uid, __u32 gid);
|
|
+yaffs_Object *yaffs_FindObjectByName(yaffs_Object *theDir,const YCHAR *name);
|
|
+int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *));
|
|
+
|
|
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number);
|
|
+
|
|
+// Link operations
|
|
+yaffs_Object *yaffs_Link(yaffs_Object *parent, const YCHAR *name, yaffs_Object *equivalentObject);
|
|
+
|
|
+yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj);
|
|
+
|
|
+// Symlink operations
|
|
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent, const YCHAR *name, __u32 mode, __u32 uid, __u32 gid, const YCHAR *alias);
|
|
+YCHAR *yaffs_GetSymlinkAlias(yaffs_Object *obj);
|
|
+
|
|
+// Special inodes (fifos, sockets and devices)
|
|
+yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const YCHAR *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);
|
|
+
|
|
+#ifdef CONFIG_YAFFS_WINCE
|
|
+// CONFIG_YAFFS_WINCE special stuff
|
|
+void yfsd_WinFileTimeNow(__u32 target[2]);
|
|
+#endif
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+
|
|
+void yaffs_HandleDeferedFree(yaffs_Object *obj);
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+// Debug dump
|
|
+int yaffs_DumpObject(yaffs_Object *obj);
|
|
+
|
|
+
|
|
+void yaffs_GutsTest(yaffs_Device *dev);
|
|
+
|
|
+
|
|
+void yaffs_InitialiseTags(yaffs_ExtendedTags *tags);
|
|
+void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND,int lyn);
|
|
+int yaffs_CheckFF(__u8 *buffer,int nBytes);
|
|
+
|
|
+#endif
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_mtdif.c b/fs/yaffs/yaffs_mtdif.c
|
|
new file mode 100644
|
|
index 0000000..90146fb
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_mtdif.c
|
|
@@ -0,0 +1,153 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ * yaffs_mtdif.c NAND mtd wrapper functions.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ */
|
|
+
|
|
+const char *yaffs_mtdif_c_version = "$Id: yaffs_mtdif.c,v 1.7 2005/08/01 20:52:35 luc Exp $";
|
|
+
|
|
+#include "yportenv.h"
|
|
+
|
|
+#ifdef CONFIG_YAFFS_YAFFS1
|
|
+
|
|
+#include "yaffs_mtdif.h"
|
|
+
|
|
+#include "linux/mtd/mtd.h"
|
|
+#include "linux/types.h"
|
|
+#include "linux/time.h"
|
|
+#include "linux/mtd/nand.h"
|
|
+
|
|
+static struct nand_oobinfo yaffs_oobinfo = {
|
|
+ .useecc = 1,
|
|
+ .eccbytes = 6,
|
|
+ .eccpos = {8, 9, 10, 13, 14, 15}
|
|
+};
|
|
+
|
|
+static struct nand_oobinfo yaffs_noeccinfo = {
|
|
+ .useecc = 0,
|
|
+};
|
|
+
|
|
+
|
|
+int nandmtd_WriteChunkToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare)
|
|
+{
|
|
+ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
|
+ size_t dummy;
|
|
+ int retval = 0;
|
|
+
|
|
+ loff_t addr = ((loff_t)chunkInNAND) * dev->nBytesPerChunk;
|
|
+
|
|
+ __u8 *spareAsBytes = (__u8 *)spare;
|
|
+
|
|
+ if(data && spare)
|
|
+ {
|
|
+ if(dev->useNANDECC)
|
|
+ retval = mtd->write_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_oobinfo);
|
|
+ else
|
|
+ retval = mtd->write_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_noeccinfo);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if(data)
|
|
+ retval = mtd->write(mtd,addr,dev->nBytesPerChunk,&dummy,data);
|
|
+ if(spare)
|
|
+ retval = mtd->write_oob(mtd,addr,YAFFS_BYTES_PER_SPARE,&dummy,spareAsBytes);
|
|
+ }
|
|
+
|
|
+ if (retval == 0)
|
|
+ return YAFFS_OK;
|
|
+ else
|
|
+ return YAFFS_FAIL;
|
|
+}
|
|
+
|
|
+int nandmtd_ReadChunkFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
|
|
+{
|
|
+ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
|
+ size_t dummy;
|
|
+ int retval = 0;
|
|
+
|
|
+ loff_t addr = ((loff_t)chunkInNAND) * dev->nBytesPerChunk;
|
|
+
|
|
+ __u8 *spareAsBytes = (__u8 *)spare;
|
|
+
|
|
+ if(data && spare)
|
|
+ {
|
|
+ if(dev->useNANDECC)
|
|
+ { // Careful, this call adds 2 ints to the end of the spare data. Calling function should
|
|
+ // allocate enough memory for spare, i.e. [YAFFS_BYTES_PER_SPARE+2*sizeof(int)].
|
|
+ retval = mtd->read_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_oobinfo);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ retval = mtd->read_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_noeccinfo);
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if(data)
|
|
+ retval = mtd->read(mtd,addr,dev->nBytesPerChunk,&dummy,data);
|
|
+ if(spare)
|
|
+ retval = mtd->read_oob(mtd,addr,YAFFS_BYTES_PER_SPARE,&dummy,spareAsBytes);
|
|
+ }
|
|
+
|
|
+ if (retval == 0)
|
|
+ return YAFFS_OK;
|
|
+ else
|
|
+ return YAFFS_FAIL;
|
|
+}
|
|
+
|
|
+// Callback not needed for NAND
|
|
+#if 0
|
|
+static void nandmtd_EraseCallback(struct erase_info *ei)
|
|
+{
|
|
+ yaffs_Device *dev = (yaffs_Device *)ei->priv;
|
|
+ up(&dev->sem);
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+int nandmtd_EraseBlockInNAND(yaffs_Device *dev, int blockNumber)
|
|
+{
|
|
+ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
|
+ __u32 addr = ((loff_t) blockNumber) * dev->nBytesPerChunk * dev->nChunksPerBlock;
|
|
+ struct erase_info ei;
|
|
+ int retval = 0;
|
|
+
|
|
+ ei.mtd = mtd;
|
|
+ ei.addr = addr;
|
|
+ ei.len = dev->nBytesPerChunk * dev->nChunksPerBlock;
|
|
+ ei.time = 1000;
|
|
+ ei.retries = 2;
|
|
+ ei.callback = NULL;
|
|
+ ei.priv = (u_long)dev;
|
|
+
|
|
+ // Todo finish off the ei if required
|
|
+
|
|
+ sema_init(&dev->sem,0);
|
|
+
|
|
+ retval = mtd->erase(mtd,&ei);
|
|
+
|
|
+ //No need for callback
|
|
+ // down(&dev->sem); // Wait for the erasure to complete
|
|
+
|
|
+ if (retval == 0)
|
|
+ return YAFFS_OK;
|
|
+ else
|
|
+ return YAFFS_FAIL;
|
|
+}
|
|
+
|
|
+int nandmtd_InitialiseNAND(yaffs_Device *dev)
|
|
+{
|
|
+ return YAFFS_OK;
|
|
+}
|
|
+
|
|
+#endif // CONFIG_YAFFS_YAFFS1
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_mtdif.h b/fs/yaffs/yaffs_mtdif.h
|
|
new file mode 100644
|
|
index 0000000..8846828
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_mtdif.h
|
|
@@ -0,0 +1,33 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ * yaffs_mtdif.h NAND mtd interface wrappers
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ *
|
|
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
+ *
|
|
+ * $Id: yaffs_mtdif.h,v 1.2 2005/07/19 20:41:59 charles Exp $
|
|
+ */
|
|
+
|
|
+#ifndef __YAFFS_MTDIF_H__
|
|
+#define __YAFFS_MTDIF_H__
|
|
+
|
|
+#include "yaffs_guts.h"
|
|
+
|
|
+int nandmtd_WriteChunkToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare);
|
|
+int nandmtd_ReadChunkFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
|
|
+int nandmtd_EraseBlockInNAND(yaffs_Device *dev, int blockNumber);
|
|
+int nandmtd_InitialiseNAND(yaffs_Device *dev);
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_mtdif2.c b/fs/yaffs/yaffs_mtdif2.c
|
|
new file mode 100644
|
|
index 0000000..0c4cc39
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_mtdif2.c
|
|
@@ -0,0 +1,177 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ * yaffs_mtdif.c NAND mtd wrapper functions.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ */
|
|
+
|
|
+// mtd interface for YAFFS2
|
|
+
|
|
+const char *yaffs_mtdif2_c_version = "$Id: yaffs_mtdif2.c,v 1.6 2005/08/01 20:52:35 luc Exp $";
|
|
+
|
|
+#include "yportenv.h"
|
|
+
|
|
+#ifdef CONFIG_YAFFS_YAFFS2
|
|
+
|
|
+#include "yaffs_mtdif2.h"
|
|
+
|
|
+#include "linux/mtd/mtd.h"
|
|
+#include "linux/types.h"
|
|
+#include "linux/time.h"
|
|
+
|
|
+#include "yaffs_packedtags2.h"
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_ExtendedTags *tags)
|
|
+{
|
|
+ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
|
+ size_t dummy;
|
|
+ int retval = 0;
|
|
+
|
|
+
|
|
+ loff_t addr = ((loff_t)chunkInNAND) * dev->nBytesPerChunk;
|
|
+
|
|
+ yaffs_PackedTags2 pt;
|
|
+
|
|
+ T(YAFFS_TRACE_MTD,(TSTR("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p" TENDSTR),chunkInNAND,data,tags));
|
|
+
|
|
+ if(tags)
|
|
+ {
|
|
+ yaffs_PackTags2(&pt,tags);
|
|
+ }
|
|
+
|
|
+ if(data && tags)
|
|
+ {
|
|
+ if(dev->useNANDECC)
|
|
+ retval = mtd->write_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,(__u8 *)&pt,NULL);
|
|
+ else
|
|
+ retval = mtd->write_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,(__u8 *)&pt,NULL);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if(data)
|
|
+ retval = mtd->write(mtd,addr,dev->nBytesPerChunk,&dummy,data);
|
|
+ if(tags)
|
|
+ retval = mtd->write_oob(mtd,addr,mtd->oobsize,&dummy,(__u8 *)&pt);
|
|
+
|
|
+ }
|
|
+
|
|
+ if (retval == 0)
|
|
+ return YAFFS_OK;
|
|
+ else
|
|
+ return YAFFS_FAIL;
|
|
+}
|
|
+
|
|
+int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_ExtendedTags *tags)
|
|
+{
|
|
+ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
|
+ size_t dummy;
|
|
+ int retval = 0;
|
|
+
|
|
+
|
|
+ loff_t addr = ((loff_t)chunkInNAND) * dev->nBytesPerChunk;
|
|
+
|
|
+ yaffs_PackedTags2 pt;
|
|
+
|
|
+ T(YAFFS_TRACE_MTD,(TSTR("nandmtd2_ReadChunkWithTagsToNAND chunk %d data %p tags %p" TENDSTR),chunkInNAND,data,tags));
|
|
+
|
|
+ if(data && tags)
|
|
+ {
|
|
+ if(dev->useNANDECC)
|
|
+ {
|
|
+ retval = mtd->read_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,dev->spareBuffer,NULL);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ retval = mtd->read_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,dev->spareBuffer,NULL);
|
|
+ }
|
|
+ memcpy(&pt,dev->spareBuffer,sizeof(pt));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if(data)
|
|
+ retval = mtd->read(mtd,addr,dev->nBytesPerChunk,&dummy,data);
|
|
+ if(tags) {
|
|
+ retval = mtd->read_oob(mtd,addr,mtd->oobsize,&dummy,dev->spareBuffer);
|
|
+ memcpy(&pt, mtd->oobinfo.oobfree[0][0] + (char *)dev->spareBuffer, sizeof(pt));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(tags)
|
|
+ yaffs_UnpackTags2(tags,&pt);
|
|
+
|
|
+ if (retval == 0)
|
|
+ return YAFFS_OK;
|
|
+ else
|
|
+ return YAFFS_FAIL;
|
|
+}
|
|
+
|
|
+int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo)
|
|
+{
|
|
+ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
|
+ int retval;
|
|
+ T(YAFFS_TRACE_MTD,(TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR),blockNo));
|
|
+
|
|
+
|
|
+ retval = mtd->block_markbad(mtd,blockNo * dev->nChunksPerBlock * dev->nBytesPerChunk);
|
|
+
|
|
+ if (retval == 0)
|
|
+ return YAFFS_OK;
|
|
+ else
|
|
+ return YAFFS_FAIL;
|
|
+
|
|
+}
|
|
+
|
|
+int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState *state, int *sequenceNumber)
|
|
+{
|
|
+ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
|
+ int retval;
|
|
+
|
|
+ T(YAFFS_TRACE_MTD,(TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR),blockNo));
|
|
+ retval = mtd->block_isbad(mtd,blockNo* dev->nChunksPerBlock * dev->nBytesPerChunk);
|
|
+
|
|
+ if(retval)
|
|
+ {
|
|
+ T(YAFFS_TRACE_MTD,(TSTR("block is bad" TENDSTR)));
|
|
+
|
|
+ *state = YAFFS_BLOCK_STATE_DEAD;
|
|
+ *sequenceNumber = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ yaffs_ExtendedTags t;
|
|
+ nandmtd2_ReadChunkWithTagsFromNAND(dev,blockNo * dev->nChunksPerBlock,NULL, &t);
|
|
+
|
|
+ if(t.chunkUsed)
|
|
+ {
|
|
+ *sequenceNumber = t.sequenceNumber;
|
|
+ *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ *sequenceNumber = 0;
|
|
+ *state = YAFFS_BLOCK_STATE_EMPTY;
|
|
+ }
|
|
+ }
|
|
+ T(YAFFS_TRACE_MTD,(TSTR("block is bad seq %d state %d" TENDSTR), *sequenceNumber,*state));
|
|
+
|
|
+ if (retval == 0)
|
|
+ return YAFFS_OK;
|
|
+ else
|
|
+ return YAFFS_FAIL;
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_mtdif2.h b/fs/yaffs/yaffs_mtdif2.h
|
|
new file mode 100644
|
|
index 0000000..5b308ee
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_mtdif2.h
|
|
@@ -0,0 +1,27 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ * yaffs_mtdif.c NAND mtd wrapper functions.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * 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_MTDIF2_H__
|
|
+#define __YAFFS_MTDIF2_H__
|
|
+
|
|
+
|
|
+#include "yaffs_guts.h"
|
|
+int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_ExtendedTags *tags);
|
|
+int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_ExtendedTags *tags);
|
|
+int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo);
|
|
+int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState *state, int *sequenceNumber);
|
|
+
|
|
+#endif
|
|
diff --git a/fs/yaffs/yaffs_nandemul2k.h b/fs/yaffs/yaffs_nandemul2k.h
|
|
new file mode 100644
|
|
index 0000000..b35d474
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_nandemul2k.h
|
|
@@ -0,0 +1,38 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ *
|
|
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
+ *
|
|
+ * yaffs_nandemul2k.h: Interface to emulated NAND functions (2k page size)
|
|
+ *
|
|
+ * $Id: yaffs_nandemul2k.h,v 1.1 2004/12/17 04:39:04 charles Exp $
|
|
+ */
|
|
+
|
|
+#ifndef __YAFFS_NANDEMUL2K_H__
|
|
+#define __YAFFS_NANDEMUL2K_H__
|
|
+
|
|
+#include "yaffs_guts.h"
|
|
+
|
|
+
|
|
+int nandemul2k_WriteChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_ExtendedTags *tags);
|
|
+int nandemul2k_ReadChunkWithTagsFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_ExtendedTags *tags);
|
|
+int nandemul2k_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo);
|
|
+int nandemul2k_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState *state, int *sequenceNumber);
|
|
+int nandemul2k_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND);
|
|
+int nandemul2k_InitialiseNAND(struct yaffs_DeviceStruct *dev);
|
|
+int nandemul2k_GetBytesPerChunk(void);
|
|
+int nandemul2k_GetChunksPerBlock(void);
|
|
+int nandemul2k_GetNumberOfBlocks(void);
|
|
+
|
|
+#endif
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_packedtags1.c b/fs/yaffs/yaffs_packedtags1.c
|
|
new file mode 100644
|
|
index 0000000..bab338f
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_packedtags1.c
|
|
@@ -0,0 +1,42 @@
|
|
+#include "yaffs_packedtags1.h"
|
|
+#include "yportenv.h"
|
|
+
|
|
+void yaffs_PackTags1(yaffs_PackedTags1 *pt, const yaffs_ExtendedTags *t)
|
|
+{
|
|
+ pt->chunkId = t->chunkId;
|
|
+ pt->serialNumber = t->serialNumber;
|
|
+ pt->byteCount = t->byteCount;
|
|
+ pt->objectId = t->objectId;
|
|
+ pt->ecc = 0;
|
|
+ pt->deleted = (t->chunkDeleted) ? 0 : 1;
|
|
+ pt->unusedStuff = 0;
|
|
+ pt->shouldBeFF = 0xFFFFFFFF;
|
|
+
|
|
+}
|
|
+
|
|
+void yaffs_UnpackTags1(yaffs_ExtendedTags *t, const yaffs_PackedTags1 *pt)
|
|
+{
|
|
+ static const __u8 allFF[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,0xff, 0xff, 0xff, 0xff};
|
|
+
|
|
+ if(memcmp(allFF,pt,sizeof(yaffs_PackedTags1)))
|
|
+ {
|
|
+ t->blockBad = 0;
|
|
+ if(pt->shouldBeFF != 0xFFFFFFFF)
|
|
+ {
|
|
+ t->blockBad = 1;
|
|
+ }
|
|
+ t->chunkUsed = 1;
|
|
+ t->objectId = pt->objectId;
|
|
+ t->chunkId = pt->chunkId;
|
|
+ t->byteCount = pt->byteCount;
|
|
+ t->eccResult = YAFFS_ECC_RESULT_NO_ERROR;
|
|
+ t->chunkDeleted = (pt->deleted) ? 0 : 1;
|
|
+ t->serialNumber = pt->serialNumber;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ memset(t,0,sizeof(yaffs_ExtendedTags));
|
|
+
|
|
+ }
|
|
+}
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_packedtags1.h b/fs/yaffs/yaffs_packedtags1.h
|
|
new file mode 100644
|
|
index 0000000..0f76615
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_packedtags1.h
|
|
@@ -0,0 +1,28 @@
|
|
+// This is used to pack YAFFS1 tags, not YAFFS2 tags.
|
|
+
|
|
+#ifndef __YAFFS_PACKEDTAGS1_H__
|
|
+#define __YAFFS_PACKEDTAGS1_H__
|
|
+
|
|
+
|
|
+#include "yaffs_guts.h"
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ unsigned chunkId:20;
|
|
+ unsigned serialNumber:2;
|
|
+ unsigned byteCount:10;
|
|
+ unsigned objectId:18;
|
|
+ unsigned ecc:12;
|
|
+ unsigned deleted:1;
|
|
+ unsigned unusedStuff:1;
|
|
+ unsigned shouldBeFF;
|
|
+
|
|
+} yaffs_PackedTags1;
|
|
+
|
|
+
|
|
+
|
|
+void yaffs_PackTags1(yaffs_PackedTags1 *pt, const yaffs_ExtendedTags *t);
|
|
+void yaffs_UnpackTags1(yaffs_ExtendedTags *t, const yaffs_PackedTags1 *pt);
|
|
+#endif
|
|
+
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_packedtags2.c b/fs/yaffs/yaffs_packedtags2.c
|
|
new file mode 100644
|
|
index 0000000..f4de18d
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_packedtags2.c
|
|
@@ -0,0 +1,164 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ *
|
|
+ * yaffs_packedtags2.c: Tags packing for YAFFS2
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Lesser General Public License
|
|
+ * version 2.1 as published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include "yaffs_packedtags2.h"
|
|
+#include "yportenv.h"
|
|
+#include "yaffs_tagsvalidity.h"
|
|
+
|
|
+
|
|
+
|
|
+// This code packs a set of extended tags into a binary structure for NAND storage
|
|
+
|
|
+// Some of the information is "extra" struff which can be packed in to speed scanning
|
|
+// This is defined by having the EXTRA_HEADER_INFO_FLAG set.
|
|
+
|
|
+
|
|
+// Extra flags applied to chunkId
|
|
+
|
|
+#define EXTRA_HEADER_INFO_FLAG 0x80000000
|
|
+#define EXTRA_SHRINK_FLAG 0x40000000
|
|
+#define EXTRA_SHADOWS_FLAG 0x20000000
|
|
+#define EXTRA_SPARE_FLAGS 0x10000000
|
|
+
|
|
+#define ALL_EXTRA_FLAGS 0xF0000000
|
|
+
|
|
+
|
|
+
|
|
+// Also, the top 4 bits of the object Id are set to the object type.
|
|
+#define EXTRA_OBJECT_TYPE_SHIFT (28)
|
|
+#define EXTRA_OBJECT_TYPE_MASK ((0x0F) << EXTRA_OBJECT_TYPE_SHIFT)
|
|
+
|
|
+
|
|
+
|
|
+static void yaffs_DumpPackedTags2(const yaffs_PackedTags2 *pt)
|
|
+{
|
|
+ T(YAFFS_TRACE_MTD,(TSTR("packed tags obj %d chunk %d byte %d seq %d"TENDSTR),pt->t.objectId,pt->t.chunkId,pt->t.byteCount,pt->t.sequenceNumber));
|
|
+}
|
|
+
|
|
+static void yaffs_DumpTags2(const yaffs_ExtendedTags *t)
|
|
+{
|
|
+ T(YAFFS_TRACE_MTD,(TSTR("ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d"TENDSTR),
|
|
+ t->eccResult, t->blockBad, t->chunkUsed, t->objectId, t->chunkId, t->byteCount, t->chunkDeleted, t->serialNumber, t->sequenceNumber));
|
|
+
|
|
+}
|
|
+
|
|
+void yaffs_PackTags2(yaffs_PackedTags2 *pt, const yaffs_ExtendedTags *t)
|
|
+{
|
|
+ pt->t.chunkId = t->chunkId;
|
|
+ pt->t.sequenceNumber = t->sequenceNumber;
|
|
+ pt->t.byteCount = t->byteCount;
|
|
+ pt->t.objectId = t->objectId;
|
|
+
|
|
+ if(t->chunkId == 0 && t->extraHeaderInfoAvailable)
|
|
+ {
|
|
+ // Store the extra header info instead
|
|
+ pt->t.chunkId = EXTRA_HEADER_INFO_FLAG | t->extraParentObjectId; // We save the parent object in the chunkId
|
|
+ if(t->extraIsShrinkHeader)
|
|
+ {
|
|
+ pt->t.chunkId |= EXTRA_SHRINK_FLAG;
|
|
+ }
|
|
+ if(t->extraShadows)
|
|
+ {
|
|
+ pt->t.chunkId |= EXTRA_SHADOWS_FLAG;
|
|
+ }
|
|
+
|
|
+ pt->t.objectId &= ~EXTRA_OBJECT_TYPE_MASK;
|
|
+ pt->t.objectId |= (t->extraObjectType << EXTRA_OBJECT_TYPE_SHIFT);
|
|
+
|
|
+ if(t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK)
|
|
+ {
|
|
+ pt->t.byteCount = t->extraEquivalentObjectId;
|
|
+ }
|
|
+ else if(t->extraObjectType == YAFFS_OBJECT_TYPE_FILE)
|
|
+ {
|
|
+ pt->t.byteCount = t->extraFileLength;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ pt->t.byteCount = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ yaffs_DumpPackedTags2(pt);
|
|
+ yaffs_DumpTags2(t);
|
|
+
|
|
+#ifndef YAFFS_IGNORE_TAGS_ECC
|
|
+ {
|
|
+ yaffs_ECCCalculateOther((unsigned char *)&pt->t,sizeof(yaffs_PackedTags2TagsPart),&pt->ecc);
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+void yaffs_UnpackTags2(yaffs_ExtendedTags *t, yaffs_PackedTags2 *pt)
|
|
+{
|
|
+
|
|
+
|
|
+ memset(t,0,sizeof(yaffs_ExtendedTags));
|
|
+
|
|
+ yaffs_InitialiseTags(t);
|
|
+
|
|
+ if(pt->t.sequenceNumber != 0xFFFFFFFF)
|
|
+ {
|
|
+ // Page is in use
|
|
+#ifdef YAFFS_IGNORE_TAGS_ECC
|
|
+ {
|
|
+ t->eccResult = 0;
|
|
+ }
|
|
+#else
|
|
+ {
|
|
+ yaffs_ECCOther ecc;
|
|
+ yaffs_ECCCalculateOther((unsigned char *)&pt->t,sizeof(yaffs_PackedTags2TagsPart),&ecc);
|
|
+ t->eccResult = yaffs_ECCCorrectOther((unsigned char *)&pt->t,sizeof(yaffs_PackedTags2TagsPart),&pt->ecc,&ecc);
|
|
+ }
|
|
+#endif
|
|
+ t->blockBad = 0;
|
|
+ t->chunkUsed = 1;
|
|
+ t->objectId = pt->t.objectId;
|
|
+ t->chunkId = pt->t.chunkId;
|
|
+ t->byteCount = pt->t.byteCount;
|
|
+ t->chunkDeleted = 0;
|
|
+ t->serialNumber = 0;
|
|
+ t->sequenceNumber = pt->t.sequenceNumber;
|
|
+
|
|
+ // Do extra header info stuff
|
|
+
|
|
+ if(pt->t.chunkId & EXTRA_HEADER_INFO_FLAG)
|
|
+ {
|
|
+ t->chunkId = 0;
|
|
+ t->byteCount = 0;
|
|
+
|
|
+ t->extraHeaderInfoAvailable = 1;
|
|
+ t->extraParentObjectId = pt->t.chunkId & (~(ALL_EXTRA_FLAGS));
|
|
+ t->extraIsShrinkHeader = (pt->t.chunkId & EXTRA_SHRINK_FLAG) ? 1 : 0;
|
|
+ t->extraShadows = (pt->t.chunkId & EXTRA_SHADOWS_FLAG) ? 1 : 0;
|
|
+ t->extraObjectType = pt->t.objectId >> EXTRA_OBJECT_TYPE_SHIFT;
|
|
+ t->objectId &= ~EXTRA_OBJECT_TYPE_MASK;
|
|
+
|
|
+ if(t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK)
|
|
+ {
|
|
+ t->extraEquivalentObjectId = pt->t.byteCount;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ t->extraFileLength = pt->t.byteCount;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ yaffs_DumpPackedTags2(pt);
|
|
+ yaffs_DumpTags2(t);
|
|
+
|
|
+}
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_packedtags2.h b/fs/yaffs/yaffs_packedtags2.h
|
|
new file mode 100644
|
|
index 0000000..564dd58
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_packedtags2.h
|
|
@@ -0,0 +1,30 @@
|
|
+// This is used to pack YAFFS2 tags, not YAFFS1tags.
|
|
+
|
|
+#ifndef __YAFFS_PACKEDTAGS2_H__
|
|
+#define __YAFFS_PACKEDTAGS2_H__
|
|
+
|
|
+
|
|
+#include "yaffs_guts.h"
|
|
+#include "yaffs_ecc.h"
|
|
+
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ unsigned sequenceNumber;
|
|
+ unsigned objectId;
|
|
+ unsigned chunkId;
|
|
+ unsigned byteCount;
|
|
+} yaffs_PackedTags2TagsPart;
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ yaffs_PackedTags2TagsPart t;
|
|
+ yaffs_ECCOther ecc;
|
|
+} yaffs_PackedTags2;
|
|
+
|
|
+
|
|
+void yaffs_PackTags2(yaffs_PackedTags2 *pt, const yaffs_ExtendedTags *t);
|
|
+void yaffs_UnpackTags2(yaffs_ExtendedTags *t, yaffs_PackedTags2 *pt);
|
|
+#endif
|
|
+
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_tagscompat.c b/fs/yaffs/yaffs_tagscompat.c
|
|
new file mode 100644
|
|
index 0000000..fdf1cbc
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_tagscompat.c
|
|
@@ -0,0 +1,698 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ * yaffs_tagscompat.h: Tags compatability layer to use YAFFS1 formatted NAND.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * $Id: yaffs_tagscompat.c,v 1.3 2005/07/31 06:47:12 marty Exp $
|
|
+ */
|
|
+
|
|
+#include "yaffs_guts.h"
|
|
+#include "yaffs_tagscompat.h"
|
|
+#include "yaffs_ecc.h"
|
|
+
|
|
+static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND);
|
|
+#ifdef NOTYET
|
|
+static void yaffs_CheckWrittenBlock(yaffs_Device *dev,int chunkInNAND);
|
|
+static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare);
|
|
+static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare);
|
|
+static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND);
|
|
+#endif
|
|
+
|
|
+
|
|
+static const char yaffs_countBitsTable[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
|
|
+};
|
|
+
|
|
+static int yaffs_CountBits(__u8 x)
|
|
+{
|
|
+ int retVal;
|
|
+ retVal = yaffs_countBitsTable[x];
|
|
+ return retVal;
|
|
+}
|
|
+
|
|
+
|
|
+/////////////// Tags ECC calculations ///////////////////
|
|
+
|
|
+void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
|
|
+{
|
|
+ yaffs_ECCCalculate(data , spare->ecc1);
|
|
+ yaffs_ECCCalculate(&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 &0xff; j<<=1)
|
|
+ {
|
|
+ bit++;
|
|
+ if(b[i] & j)
|
|
+ {
|
|
+ ecc ^= bit;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ tags->ecc = ecc;
|
|
+
|
|
+
|
|
+}
|
|
+
|
|
+int yaffs_CheckECCOnTags(yaffs_Tags *tags)
|
|
+{
|
|
+ unsigned ecc = tags->ecc;
|
|
+
|
|
+ yaffs_CalcTagsECC(tags);
|
|
+
|
|
+ ecc ^= tags->ecc;
|
|
+
|
|
+ if(ecc && ecc <= 64)
|
|
+ {
|
|
+ // TODO: Handle the failure better. Retire?
|
|
+ unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
|
|
+
|
|
+ ecc--;
|
|
+
|
|
+ b[ecc / 8] ^= (1 << (ecc & 7));
|
|
+
|
|
+ // Now recvalc the ecc
|
|
+ yaffs_CalcTagsECC(tags);
|
|
+
|
|
+ return 1; // recovered error
|
|
+ }
|
|
+ else if(ecc)
|
|
+ {
|
|
+ // Wierd ecc failure value
|
|
+ // TODO Need to do somethiong here
|
|
+ return -1; //unrecovered error
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+//////////////////////////// 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_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)
|
|
+{
|
|
+ yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
|
|
+ int result;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ result = yaffs_CheckECCOnTags(tagsPtr);
|
|
+ if(result> 0)
|
|
+ {
|
|
+ dev->tagsEccFixed++;
|
|
+ }
|
|
+ else if(result <0)
|
|
+ {
|
|
+ dev->tagsEccUnfixed++;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void yaffs_SpareInitialise(yaffs_Spare *spare)
|
|
+{
|
|
+ memset(spare,0xFF,sizeof(yaffs_Spare));
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
|
|
+{
|
|
+ if(chunkInNAND < dev->startBlock * dev->nChunksPerBlock)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND));
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+
|
|
+ dev->nPageWrites++;
|
|
+ return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
|
|
+ int chunkInNAND,
|
|
+ __u8 *data,
|
|
+ yaffs_Spare *spare,
|
|
+ yaffs_ECCResult *eccResult,
|
|
+ int doErrorCorrection)
|
|
+{
|
|
+ int retVal;
|
|
+ yaffs_Spare localSpare;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+
|
|
+ if(!dev->useNANDECC)
|
|
+ {
|
|
+ retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
|
|
+ if(data && doErrorCorrection)
|
|
+ {
|
|
+ // Do ECC correction
|
|
+ //Todo handle any errors
|
|
+ int eccResult1,eccResult2;
|
|
+ __u8 calcEcc[3];
|
|
+
|
|
+ yaffs_ECCCalculate(data,calcEcc);
|
|
+ eccResult1 = yaffs_ECCCorrect (data,spare->ecc1, calcEcc);
|
|
+ yaffs_ECCCalculate(&data[256],calcEcc);
|
|
+ eccResult2 = yaffs_ECCCorrect(&data[256],spare->ecc2, calcEcc);
|
|
+
|
|
+ if(eccResult1>0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR, (TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
|
|
+ dev->eccFixed++;
|
|
+ }
|
|
+ else if(eccResult1<0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
|
|
+ dev->eccUnfixed++;
|
|
+ }
|
|
+
|
|
+ if(eccResult2>0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
|
|
+ dev->eccFixed++;
|
|
+ }
|
|
+ else if(eccResult2<0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
|
|
+ dev->eccUnfixed++;
|
|
+ }
|
|
+
|
|
+ if(eccResult1 || eccResult2)
|
|
+ {
|
|
+ // Hoosterman, we had a data problem on this page
|
|
+ yaffs_HandleReadDataError(dev,chunkInNAND);
|
|
+ }
|
|
+
|
|
+ if(eccResult1 < 0 || eccResult2 < 0)
|
|
+ *eccResult = YAFFS_ECC_RESULT_UNFIXED;
|
|
+ else if(eccResult1 > 0 || eccResult2 > 0)
|
|
+ *eccResult = YAFFS_ECC_RESULT_FIXED;
|
|
+ else
|
|
+ *eccResult = YAFFS_ECC_RESULT_NO_ERROR;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // Must allocate enough memory for spare+2*sizeof(int) for ecc results from device.
|
|
+ struct yaffs_NANDSpare nspare;
|
|
+ retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare*)&nspare);
|
|
+ memcpy (spare, &nspare, sizeof(yaffs_Spare));
|
|
+ if(data && doErrorCorrection)
|
|
+ {
|
|
+ if(nspare.eccres1>0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
|
|
+ }
|
|
+ else if(nspare.eccres1<0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
|
|
+ }
|
|
+
|
|
+ if(nspare.eccres2>0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
|
|
+ }
|
|
+ else if(nspare.eccres2<0)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
|
|
+ }
|
|
+
|
|
+ if(nspare.eccres1 || nspare.eccres2)
|
|
+ {
|
|
+ // Hoosterman, we had a data problem on this page
|
|
+ yaffs_HandleReadDataError(dev,chunkInNAND);
|
|
+ }
|
|
+
|
|
+ if(nspare.eccres1 < 0 || nspare.eccres2 < 0)
|
|
+ *eccResult = YAFFS_ECC_RESULT_UNFIXED;
|
|
+ else if(nspare.eccres1 > 0 || nspare.eccres2 > 0)
|
|
+ *eccResult = YAFFS_ECC_RESULT_FIXED;
|
|
+ else
|
|
+ *eccResult = YAFFS_ECC_RESULT_NO_ERROR;
|
|
+
|
|
+
|
|
+ }
|
|
+ }
|
|
+ return retVal;
|
|
+}
|
|
+
|
|
+#ifdef NOTYET
|
|
+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];
|
|
+ // Might as well always allocate the larger size for dev->useNANDECC == true;
|
|
+ static __u8 spare[sizeof(struct yaffs_NANDSpare)];
|
|
+
|
|
+ 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)) return YAFFS_FAIL;
|
|
+ if(memcmp(cmpbuf,spare,16)) return YAFFS_FAIL;
|
|
+
|
|
+
|
|
+ return YAFFS_OK;
|
|
+
|
|
+}
|
|
+#endif
|
|
+
|
|
+#if 0
|
|
+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);
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+#if 0
|
|
+static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_Spare *spare,int useReserve)
|
|
+{
|
|
+ int chunk;
|
|
+
|
|
+ int writeOk = 1;
|
|
+ int attempts = 0;
|
|
+
|
|
+ unsigned char rbData[YAFFS_BYTES_PER_CHUNK];
|
|
+ yaffs_Spare rbSpare;
|
|
+
|
|
+ do{
|
|
+ chunk = yaffs_AllocateChunk(dev,useReserve);
|
|
+
|
|
+ if(chunk >= 0)
|
|
+ {
|
|
+
|
|
+ // First check this chunk is erased...
|
|
+#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
|
|
+ writeOk = yaffs_CheckChunkErased(dev,chunk);
|
|
+#endif
|
|
+ if(!writeOk)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ 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.
|
|
+ // NB We check a raw read without ECC correction applied
|
|
+ yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare,0);
|
|
+
|
|
+#ifndef CONFIG_YAFFS_DISABLE_WRITE_VERIFY
|
|
+ if(!yaffs_VerifyCompare(data,rbData,spare,&rbSpare))
|
|
+ {
|
|
+ // Didn't verify
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk));
|
|
+
|
|
+ writeOk = 0;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ }
|
|
+ if(writeOk)
|
|
+ {
|
|
+ // Copy the data into the write buffer.
|
|
+ // NB We do this at the end to prevent duplicates in the case of a write error.
|
|
+ //Todo
|
|
+ yaffs_HandleWriteChunkOk(dev,chunk,data,spare);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ yaffs_HandleWriteChunkError(dev,chunk);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ } while(chunk >= 0 && ! writeOk);
|
|
+
|
|
+ if(attempts > 1)
|
|
+ {
|
|
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));
|
|
+ dev->nRetriedWrites+= (attempts - 1);
|
|
+ }
|
|
+
|
|
+ return chunk;
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+///
|
|
+// Functions for robustisizing
|
|
+//
|
|
+//
|
|
+#if 0
|
|
+
|
|
+static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND)
|
|
+{
|
|
+ // Ding the blockStatus in the first two pages of the block.
|
|
+
|
|
+ yaffs_Spare spare;
|
|
+
|
|
+ memset(&spare, 0xff,sizeof(yaffs_Spare));
|
|
+
|
|
+ spare.blockStatus = 0;
|
|
+
|
|
+ // TODO change this retirement marking for other NAND types
|
|
+ yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL , &spare);
|
|
+ yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1, NULL , &spare);
|
|
+
|
|
+ yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD;
|
|
+ dev->nRetiredBlocks++;
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+#if 0
|
|
+static int yaffs_RewriteBufferedBlock(yaffs_Device *dev)
|
|
+{
|
|
+ dev->doingBufferedBlockRewrite = 1;
|
|
+ //
|
|
+ // Remove erased chunks
|
|
+ // Rewrite existing chunks to a new block
|
|
+ // Set current write block to the new block
|
|
+
|
|
+ dev->doingBufferedBlockRewrite = 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND)
|
|
+{
|
|
+ int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
|
|
+
|
|
+ // Mark the block for retirement
|
|
+ yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
|
|
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND));
|
|
+
|
|
+
|
|
+ //TODO
|
|
+ // Just do a garbage collection on the affected block then retire the block
|
|
+ // NB recursion
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef NOTYET
|
|
+static void yaffs_CheckWrittenBlock(yaffs_Device *dev,int chunkInNAND)
|
|
+{
|
|
+}
|
|
+
|
|
+static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare)
|
|
+{
|
|
+}
|
|
+
|
|
+static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare)
|
|
+{
|
|
+}
|
|
+
|
|
+static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)
|
|
+{
|
|
+ int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
|
|
+
|
|
+ // Mark the block for retirement
|
|
+ yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
|
|
+ // Delete the chunk
|
|
+ yaffs_DeleteChunk(dev,chunkInNAND,1,__LINE__);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1)
|
|
+{
|
|
+
|
|
+
|
|
+ if( memcmp(d0,d1,YAFFS_BYTES_PER_CHUNK) != 0 ||
|
|
+ s0->tagByte0 != s1->tagByte0 ||
|
|
+ s0->tagByte1 != s1->tagByte1 ||
|
|
+ s0->tagByte2 != s1->tagByte2 ||
|
|
+ s0->tagByte3 != s1->tagByte3 ||
|
|
+ s0->tagByte4 != s1->tagByte4 ||
|
|
+ s0->tagByte5 != s1->tagByte5 ||
|
|
+ s0->tagByte6 != s1->tagByte6 ||
|
|
+ s0->tagByte7 != s1->tagByte7 ||
|
|
+ s0->ecc1[0] != s1->ecc1[0] ||
|
|
+ s0->ecc1[1] != s1->ecc1[1] ||
|
|
+ s0->ecc1[2] != s1->ecc1[2] ||
|
|
+ s0->ecc2[0] != s1->ecc2[0] ||
|
|
+ s0->ecc2[1] != s1->ecc2[1] ||
|
|
+ s0->ecc2[2] != s1->ecc2[2] )
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+#endif /* NOTYET */
|
|
+
|
|
+#if 0
|
|
+typedef struct
|
|
+{
|
|
+
|
|
+ unsigned validMarker0;
|
|
+ unsigned chunkUsed; // Status of the chunk: used or unused
|
|
+ unsigned objectId; // If 0 then this is not part of an object (unused)
|
|
+ unsigned chunkId; // If 0 then this is a header
|
|
+ unsigned byteCount; // Only valid for data chunks
|
|
+ // The following stuff only has meaning when we read
|
|
+ yaffs_ECCResult eccResult; // Only valid when we read.
|
|
+ unsigned blockBad; // Only valid on reading
|
|
+
|
|
+ // YAFFS 1 stuff
|
|
+ unsigned chunkDeleted; // The chunk is marked deleted
|
|
+ unsigned serialNumber; // Yaffs1 2-bit serial number
|
|
+
|
|
+ // YAFFS2 stuff
|
|
+ unsigned sequenceNumber; // The sequence number of this block
|
|
+
|
|
+ unsigned validMarker1;
|
|
+
|
|
+} yaffs_ExtendedTags;
|
|
+
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ unsigned chunkId:20;
|
|
+ unsigned serialNumber:2;
|
|
+ unsigned byteCount:10;
|
|
+ unsigned objectId:18;
|
|
+ unsigned ecc:12;
|
|
+ unsigned unusedStuff:2;
|
|
+} yaffs_Tags;
|
|
+
|
|
+
|
|
+#endif
|
|
+
|
|
+int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_ExtendedTags *eTags)
|
|
+{
|
|
+ yaffs_Spare spare;
|
|
+ yaffs_Tags tags;
|
|
+
|
|
+ yaffs_SpareInitialise(&spare);
|
|
+
|
|
+ if(eTags->chunkDeleted)
|
|
+ {
|
|
+ spare.pageStatus = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ tags.objectId = eTags->objectId;
|
|
+ tags.chunkId = eTags->chunkId;
|
|
+ tags.byteCount = eTags->byteCount;
|
|
+ tags.serialNumber = eTags->serialNumber;
|
|
+
|
|
+// NCB
|
|
+ if (!dev->useNANDECC && data)
|
|
+ {
|
|
+ yaffs_CalcECC(data,&spare);
|
|
+ }
|
|
+
|
|
+// /NCB
|
|
+ yaffs_LoadTagsIntoSpare(&spare,&tags);
|
|
+
|
|
+ }
|
|
+
|
|
+ return yaffs_WriteChunkToNAND(dev,chunkInNAND,data,&spare);
|
|
+}
|
|
+
|
|
+
|
|
+int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_ExtendedTags *eTags)
|
|
+{
|
|
+
|
|
+ yaffs_Spare spare;
|
|
+ yaffs_Tags tags;
|
|
+ yaffs_ECCResult eccResult;
|
|
+
|
|
+// NCB
|
|
+ static yaffs_Spare spareFF;
|
|
+ static int init;
|
|
+
|
|
+ if(!init)
|
|
+ {
|
|
+ memset(&spareFF,0xFF,sizeof(spareFF));
|
|
+ init = 1;
|
|
+ }
|
|
+// /NCB
|
|
+ if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,data,&spare,&eccResult,1))
|
|
+ {
|
|
+// added NCB - eTags may be NULL
|
|
+ if (eTags) {
|
|
+
|
|
+ int deleted = (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0;
|
|
+
|
|
+ yaffs_GetTagsFromSpare(dev,&spare,&tags);
|
|
+
|
|
+ eTags->chunkDeleted = deleted;
|
|
+ eTags->objectId = tags.objectId;
|
|
+ eTags->chunkId = tags.chunkId;
|
|
+ eTags->byteCount = tags.byteCount;
|
|
+ eTags->serialNumber = tags.serialNumber;
|
|
+ eTags->eccResult = eccResult;
|
|
+ eTags->blockBad = 0; // We're reading it therefore it is not a bad block
|
|
+
|
|
+// NCB added 18/2/2005
|
|
+ eTags->chunkUsed = (memcmp(&spareFF,&spare,sizeof(spareFF)) != 0) ? 1:0;
|
|
+ }
|
|
+
|
|
+ return YAFFS_OK;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return YAFFS_FAIL;
|
|
+ }
|
|
+}
|
|
+
|
|
+int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockInNAND)
|
|
+{
|
|
+
|
|
+ yaffs_Spare spare;
|
|
+
|
|
+ memset(&spare, 0xff,sizeof(yaffs_Spare));
|
|
+
|
|
+ spare.blockStatus = 0;
|
|
+
|
|
+ yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL , &spare);
|
|
+ yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1, NULL , &spare);
|
|
+
|
|
+ return YAFFS_OK;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState *state, int *sequenceNumber)
|
|
+{
|
|
+
|
|
+ yaffs_Spare spare0,spare1;
|
|
+ static yaffs_Spare spareFF;
|
|
+ static int init;
|
|
+ yaffs_ECCResult dummy;
|
|
+
|
|
+ if(!init)
|
|
+ {
|
|
+ memset(&spareFF,0xFF,sizeof(spareFF));
|
|
+ init = 1;
|
|
+ }
|
|
+
|
|
+ *sequenceNumber = 0;
|
|
+
|
|
+ yaffs_ReadChunkFromNAND(dev,blockNo * dev->nChunksPerBlock,NULL,&spare0,&dummy,1);
|
|
+ yaffs_ReadChunkFromNAND(dev,blockNo * dev->nChunksPerBlock + 1,NULL,&spare1,&dummy,1);
|
|
+
|
|
+ if(yaffs_CountBits(spare0.blockStatus & spare1.blockStatus) < 7)
|
|
+ *state = YAFFS_BLOCK_STATE_DEAD;
|
|
+ else if(memcmp(&spareFF,&spare0,sizeof(spareFF)) == 0)
|
|
+ *state = YAFFS_BLOCK_STATE_EMPTY;
|
|
+ else
|
|
+ *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
|
|
+
|
|
+ return YAFFS_OK;
|
|
+}
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_tagscompat.h b/fs/yaffs/yaffs_tagscompat.h
|
|
new file mode 100644
|
|
index 0000000..20b627f
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_tagscompat.h
|
|
@@ -0,0 +1,30 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ * yaffs_ramdisk.h: yaffs ram disk component
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * $Id: yaffs_tagscompat.h,v 1.1 2004/11/03 08:14:07 charles Exp $
|
|
+ */
|
|
+
|
|
+// This provides a rma disk under yaffs.
|
|
+// NB this is not intended for NAND emulation.
|
|
+// Use this with dev->useNANDECC enabled, then ECC overheads are not required.
|
|
+
|
|
+#ifndef __YAFFS_TAGSCOMPAT_H__
|
|
+#define __YAFFS_TAGSCOMPAT_H__
|
|
+
|
|
+
|
|
+#include "yaffs_guts.h"
|
|
+int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_ExtendedTags *tags);
|
|
+int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_ExtendedTags *tags);
|
|
+int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo);
|
|
+int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState *state, int *sequenceNumber);
|
|
+
|
|
+#endif
|
|
diff --git a/fs/yaffs/yaffs_tagsvalidity.c b/fs/yaffs/yaffs_tagsvalidity.c
|
|
new file mode 100644
|
|
index 0000000..2cbbeaa
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_tagsvalidity.c
|
|
@@ -0,0 +1,35 @@
|
|
+
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * $Id: yaffs_tagsvalidity.c,v 1.1 2005/04/29 18:09:16 charles Exp $
|
|
+ */
|
|
+//yaffs_tagsvalidity.c
|
|
+
|
|
+#include "yaffs_tagsvalidity.h"
|
|
+
|
|
+
|
|
+
|
|
+void yaffs_InitialiseTags(yaffs_ExtendedTags *tags)
|
|
+{
|
|
+ memset(tags,0,sizeof(yaffs_ExtendedTags));
|
|
+ tags->validMarker0 = 0xAAAAAAAA;
|
|
+ tags->validMarker1 = 0x55555555;
|
|
+}
|
|
+
|
|
+int yaffs_ValidateTags(yaffs_ExtendedTags *tags)
|
|
+{
|
|
+ return (tags->validMarker0 == 0xAAAAAAAA &&
|
|
+ tags->validMarker1 == 0x55555555);
|
|
+
|
|
+}
|
|
+
|
|
diff --git a/fs/yaffs/yaffs_tagsvalidity.h b/fs/yaffs/yaffs_tagsvalidity.h
|
|
new file mode 100644
|
|
index 0000000..26673d1
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffs_tagsvalidity.h
|
|
@@ -0,0 +1,27 @@
|
|
+
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * $Id: yaffs_tagsvalidity.h,v 1.1 2005/04/29 18:09:16 charles Exp $
|
|
+ */
|
|
+//yaffs_tagsvalidity.h
|
|
+
|
|
+
|
|
+#ifndef __YAFFS_TAGS_VALIDITY_H__
|
|
+#define __YAFFS_TAGS_VALIDITY_H__
|
|
+
|
|
+#include "yaffs_guts.h"
|
|
+
|
|
+void yaffs_InitialiseTags(yaffs_ExtendedTags *tags);
|
|
+int yaffs_ValidateTags(yaffs_ExtendedTags *tags);
|
|
+#endif
|
|
+
|
|
diff --git a/fs/yaffs/yaffsinterface.h b/fs/yaffs/yaffsinterface.h
|
|
new file mode 100644
|
|
index 0000000..e4b0ff1
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yaffsinterface.h
|
|
@@ -0,0 +1,25 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ * yaffsinterface.h: Interface to the guts of yaffs.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __YAFFSINTERFACE_H__
|
|
+#define __YAFFSINTERFACE_H__
|
|
+
|
|
+
|
|
+int yaffs_Initialise(unsigned nBlocks);
|
|
+
|
|
+#endif
|
|
+
|
|
diff --git a/fs/yaffs/yportenv.h b/fs/yaffs/yportenv.h
|
|
new file mode 100644
|
|
index 0000000..13c63bb
|
|
--- /dev/null
|
|
+++ b/fs/yaffs/yportenv.h
|
|
@@ -0,0 +1,165 @@
|
|
+/*
|
|
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
|
|
+ * yportenv.h: Portable services used by yaffs. This is done to allow
|
|
+ * simple migration from kernel space into app space for testing.
|
|
+ *
|
|
+ * Copyright (C) 2002 Aleph One Ltd.
|
|
+ * for Toby Churchill Ltd and Brightstar Engineering
|
|
+ *
|
|
+ * Created by Charles Manning <charles@aleph1.co.uk>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ *
|
|
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
|
+ *
|
|
+ * $Id: yportenv.h,v 1.6 2005/07/31 00:26:57 charles Exp $
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __YPORTENV_H__
|
|
+#define __YPORTENV_H__
|
|
+
|
|
+
|
|
+#if defined CONFIG_YAFFS_WINCE
|
|
+
|
|
+#include "ywinceenv.h"
|
|
+
|
|
+#elif defined __KERNEL__
|
|
+
|
|
+
|
|
+
|
|
+// Linux kernel
|
|
+#include <linux/config.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+#define YCHAR char
|
|
+#define YUCHAR unsigned char
|
|
+#define _Y(x) x
|
|
+#define yaffs_strcpy(a,b) strcpy(a,b)
|
|
+#define yaffs_strncpy(a,b,c) strncpy(a,b,c)
|
|
+#define yaffs_strlen(s) strlen(s)
|
|
+#define yaffs_sprintf sprintf
|
|
+#define yaffs_toupper(a) toupper(a)
|
|
+
|
|
+#define Y_INLINE inline
|
|
+
|
|
+#define YAFFS_LOSTNFOUND_NAME "lost+found"
|
|
+#define YAFFS_LOSTNFOUND_PREFIX "obj"
|
|
+
|
|
+//#define YPRINTF(x) printk x
|
|
+#define YMALLOC(x) kmalloc(x,GFP_KERNEL)
|
|
+#define YFREE(x) kfree(x)
|
|
+
|
|
+#define YAFFS_ROOT_MODE 0666
|
|
+#define YAFFS_LOSTNFOUND_MODE 0666
|
|
+
|
|
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
|
+#define Y_CURRENT_TIME CURRENT_TIME.tv_sec
|
|
+#define Y_TIME_CONVERT(x) (x).tv_sec
|
|
+#else
|
|
+#define Y_CURRENT_TIME CURRENT_TIME
|
|
+#define Y_TIME_CONVERT(x) (x)
|
|
+#endif
|
|
+
|
|
+#define yaffs_SumCompare(x,y) ((x) == (y))
|
|
+#define yaffs_strcmp(a,b) strcmp(a,b)
|
|
+
|
|
+#define TENDSTR "\n"
|
|
+#define TSTR(x) KERN_WARNING x
|
|
+#define TOUT(p) printk p
|
|
+
|
|
+
|
|
+#elif defined CONFIG_YAFFS_DIRECT
|
|
+
|
|
+// Direct interface
|
|
+#include "ydirectenv.h"
|
|
+
|
|
+#elif defined CONFIG_YAFFS_UTIL
|
|
+
|
|
+// Stuff for YAFFS utilities
|
|
+
|
|
+#include "stdlib.h"
|
|
+#include "stdio.h"
|
|
+#include "string.h"
|
|
+
|
|
+#include "devextras.h"
|
|
+
|
|
+#define YMALLOC(x) malloc(x)
|
|
+#define YFREE(x) free(x)
|
|
+
|
|
+
|
|
+#define YCHAR char
|
|
+#define YUCHAR unsigned char
|
|
+#define _Y(x) x
|
|
+#define yaffs_strcpy(a,b) strcpy(a,b)
|
|
+#define yaffs_strncpy(a,b,c) strncpy(a,b,c)
|
|
+#define yaffs_strlen(s) strlen(s)
|
|
+#define yaffs_sprintf sprintf
|
|
+#define yaffs_toupper(a) toupper(a)
|
|
+
|
|
+#define Y_INLINE inline
|
|
+
|
|
+//#define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s))
|
|
+//#define YALERT(s) YINFO(s)
|
|
+
|
|
+
|
|
+#define TENDSTR "\n"
|
|
+#define TSTR(x) x
|
|
+#define TOUT(p) printf p
|
|
+
|
|
+
|
|
+#define YAFFS_LOSTNFOUND_NAME "lost+found"
|
|
+#define YAFFS_LOSTNFOUND_PREFIX "obj"
|
|
+//#define YPRINTF(x) printf x
|
|
+
|
|
+
|
|
+#define YAFFS_ROOT_MODE 0666
|
|
+#define YAFFS_LOSTNFOUND_MODE 0666
|
|
+
|
|
+#define yaffs_SumCompare(x,y) ((x) == (y))
|
|
+#define yaffs_strcmp(a,b) strcmp(a,b)
|
|
+
|
|
+#else
|
|
+// Should have specified a configuration type
|
|
+#error Unknown configuration
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+extern unsigned yaffs_traceMask;
|
|
+
|
|
+#define YAFFS_TRACE_ERROR 0x00000001
|
|
+#define YAFFS_TRACE_OS 0x00000002
|
|
+#define YAFFS_TRACE_ALLOCATE 0x00000004
|
|
+#define YAFFS_TRACE_SCAN 0x00000008
|
|
+#define YAFFS_TRACE_BAD_BLOCKS 0x00000010
|
|
+#define YAFFS_TRACE_ERASE 0x00000020
|
|
+#define YAFFS_TRACE_GC 0x00000040
|
|
+#define YAFFS_TRACE_WRITE 0x00000080
|
|
+#define YAFFS_TRACE_TRACING 0x00000100
|
|
+#define YAFFS_TRACE_DELETION 0x00000200
|
|
+#define YAFFS_TRACE_BUFFERS 0x00000400
|
|
+#define YAFFS_TRACE_NANDACCESS 0x00000800
|
|
+#define YAFFS_TRACE_GC_DETAIL 0x00001000
|
|
+#define YAFFS_TRACE_SCAN_DEBUG 0x00002000
|
|
+#define YAFFS_TRACE_MTD 0x00004000
|
|
+#define YAFFS_TRACE_ALWAYS 0x40000000
|
|
+#define YAFFS_TRACE_BUG 0x80000000
|
|
+
|
|
+#define T(mask,p) do{ if((mask) & (yaffs_traceMask | YAFFS_TRACE_ERROR)) TOUT(p);} while(0)
|
|
+
|
|
+
|
|
+#ifndef CONFIG_YAFFS_WINCE
|
|
+#define YBUG() T(YAFFS_TRACE_BUG,(TSTR("==>> yaffs bug: " __FILE__ " %d" TENDSTR),__LINE__))
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
|