From aa39fbf7616e8b26833a09cb64c6a40859494599 Mon Sep 17 00:00:00 2001 From: charles Date: Mon, 12 Aug 2002 23:45:18 +0000 Subject: [PATCH] *** empty log message *** --- Documentation/yaffs-todo.html | 30 +- devextras.h | 524 +-- snMakefile | 4 +- utils/Makefile | 22 +- utils/mkyaffs | Bin 6382 -> 15115 bytes utils/mkyaffsimage.c | 440 +++ yaffs_fileem.c | 32 +- yaffs_fs.c | 18 +- yaffs_guts.c | 6838 +++++++++++++++++---------------- yaffs_guts.h | 910 ++--- yaffsdev | Bin 104883 -> 109461 bytes yaffsdev.c | 163 +- yaffsdev.proj | Bin 36864 -> 36864 bytes yportenv.h | 192 +- 14 files changed, 5077 insertions(+), 4096 deletions(-) create mode 100644 utils/mkyaffsimage.c diff --git a/Documentation/yaffs-todo.html b/Documentation/yaffs-todo.html index 8489d72..59ebd0a 100644 --- a/Documentation/yaffs-todo.html +++ b/Documentation/yaffs-todo.html @@ -7,18 +7,24 @@ - + -

YAFFS Todo as at 08/06/2002

+

YAFFS Todo as at 09/07/2002

Stuff not yet done

  1. Directory locking during directory walk.

    -
  2. Other locking....

    -
  3. Test disk full condition. Might not be being handled - correctly.

    -
  4. Add write memory mapping. We probably need this to support +

  5. Other locking.... investigate what is needed.

    +
  6. Test disk full condition.... investigate might not be being + handled correctly.

    +
  7. Add write memory mapping. We probably need this to support loop mounting.

    +
  8. At scan time, check that the file size in the ObjectHeader + matches the filesize of the scanned blocks.

    +
+

Tools to be done

+
    +

Recently done with no known problems (ie. probably needs significant testing)

@@ -36,6 +42,16 @@ significant testing) mot such a good thing to do with newer NANDs which do not like this sort of thing.

+
    +
  1. File pruning is done in reverse order so that if power is + lost part-way through, the file does not have any holes.

    +
+
    +
  1. mkyaffsimage. Tool to make a yaffs image from a directory (in + the spirit of mkcramfs).

    +
  2. Added support for special inodes (pipes, character & + block devices, sockets).

    +

Done, but currently known to be broken

    nothing.

    @@ -44,7 +60,7 @@ significant testing)
    1. Discuss improved NAND page interface with mtd group. This has actually started.

      -
    2. Pull out all YAFFS_OK and YAFFS_FAIL style errors and return +

    3. Pull out all YAFFS_OK and YAFFS_FAIL style errors and return with -ENOMEM style error messages.



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

    )th=PL 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 diff --git a/yaffsdev.c b/yaffsdev.c index 9675dab..0f40974 100644 --- a/yaffsdev.c +++ b/yaffsdev.c @@ -14,6 +14,8 @@ */ #include "yaffsinterface.h" +#include "yportenv.h" + #if YAFFS_FILEEM #include "yaffs_fileem.h" #else @@ -26,18 +28,10 @@ #include #include - - - yaffs_Device device; - - - - - char *testStr = "this is a test string"; char *testStr2 = "abcdefghijklmnopqrstuvwxyz1234567890"; @@ -123,8 +117,10 @@ void TestTimeasasas(yaffs_Device *dev) yaffs_DumpObject(f); - printf("Resize\n"); - yaffs_ResizeFile(f,2000); + printf("Resize to 3000\n"); + yaffs_ResizeFile(f,3000); + printf("Resize to 2048\n"); + yaffs_ResizeFile(f,2048); yaffs_DumpObject(f); @@ -181,7 +177,7 @@ void TestTime(yaffs_Device *dev) x = yaffs_RenameObject(yaffs_Root(dev),"Name1",NULL,"Rename"); for(i = 0; i < 100000; i+=20) - { + { b++; if(b & 1) @@ -190,15 +186,32 @@ void TestTime(yaffs_Device *dev) written = yaffs_WriteDataToFile(f,testStr2,i,strlen(testStr2)); } + yaffs_ReadDataFromFile(f,data,1000,50); + data[50] = 0; + + printf("Read data is \"%s\"\n",data); + yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject); + yaffs_ReadDataFromFile(f,data,1000,50); + data[50] = 0; + + printf("Read data is \"%s\"\n",data); printf("Flush\n"); yaffs_FlushFile(f); + yaffs_ReadDataFromFile(f,data,1000,50); + data[50] = 0; + + printf("Read data is \"%s\"\n",data); printf("File length is %d\n",yaffs_GetObjectFileLength(f)); sl = yaffs_MknodSymLink(yaffs_Root(dev),"sym-link",0,0,0,"/tmp/alias"); + yaffs_ReadDataFromFile(f,data,1000,50); + data[50] = 0; + + printf("Read data is \"%s\"\n",data); yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject); @@ -217,7 +230,18 @@ void TestTime(yaffs_Device *dev) yaffs_DumpObject(f); - printf("Resize\n"); + printf("Resize 3000\n"); + yaffs_ResizeFile(f,3000); + + printf("Resize 2050\n"); + yaffs_ResizeFile(f,2050); + printf("Resize 2049\n"); + yaffs_ResizeFile(f,2049); + printf("Resize 2048\n"); + yaffs_ResizeFile(f,2048); + + + printf("Resize 2000\n"); yaffs_ResizeFile(f,2000); yaffs_DumpObject(f); @@ -299,13 +323,118 @@ void TestTime(yaffs_Device *dev) yaffs_Link(yaffs_Root(dev),"phl4",f); } +void TestTimeDeleteFocussed(yaffs_Device *dev) +{ + yaffs_Object *f; + yaffs_Object *lnf; + + + int x; + int i; + int b; + int written; + + + printf("Exisiting objects\n"); + yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject); + printf("Exisiting objects in lost+found\n"); + lnf = yaffs_FindObjectByName(yaffs_Root(dev),YAFFS_LOSTNFOUND_NAME); + yaffs_ApplyToDirectoryChildren(lnf,yaffs_DumpObject); + + printf("Start\n"); + + + + + f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1"); + if(f) + { + printf("Found\n"); + } + else + { + f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0); + printf("Created\n"); + } + + + x = yaffs_RenameObject(yaffs_Root(dev),"Name1",NULL,"Rename"); + + for(i = 0; i < 100000; i+=20) + { + + b++; + if(b & 1) + written = yaffs_WriteDataToFile(f,testStr,i,strlen(testStr)); + else + written = yaffs_WriteDataToFile(f,testStr2,i,strlen(testStr2)); + } + + + + yaffs_FlushFile(f); + + + printf("Unlink file: %d\n",yaffs_Unlink(yaffs_Root(dev),"Rename")); + + yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject); + +} + +void TestTimeTnodeFocussed(yaffs_Device *dev) +{ + yaffs_Object *f; + yaffs_Object *lnf; + + + int x; + int i; + int b; + int written; + + + printf("Exisiting objects\n"); + yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject); + printf("Exisiting objects in lost+found\n"); + lnf = yaffs_FindObjectByName(yaffs_Root(dev),YAFFS_LOSTNFOUND_NAME); + yaffs_ApplyToDirectoryChildren(lnf,yaffs_DumpObject); + + printf("Start\n"); + + + + + f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1"); + if(f) + { + printf("Found\n"); + } + else + { + f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0); + printf("Created\n"); + } + + + x = yaffs_RenameObject(yaffs_Root(dev),"Name1",NULL,"Rename"); + + for(i = 0; i < 10000; i+=20) + { + + b++; + if(b & 1) + written = yaffs_WriteDataToFile(f,testStr,0,strlen(testStr)); + else + written = yaffs_WriteDataToFile(f,testStr2,0,strlen(testStr2)); + } + +} + int main(int argc,char *argv[]) { - device.nBlocks = (2 * 1024 * 1024) / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); - device.startBlock = 1; // Don't use block 0 - device.endBlock = device.nBlocks - 1; #if YAFFS_FILEEM + device.nBlocks = (64 * 1024 * 1024) / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); device.writeChunkToNAND = yaffs_FEWriteChunkToNAND; device.readChunkFromNAND = yaffs_FEReadChunkFromNAND; device.eraseBlockInNAND = yaffs_FEEraseBlockInNAND; @@ -313,6 +442,7 @@ int main(int argc,char *argv[]) printf("Testing on file emulation\n"); #else + device.nBlocks = (2 * 1024 * 1024) / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); device.writeChunkToNAND = nandemul_WriteChunkToNAND; device.readChunkFromNAND = nandemul_ReadChunkFromNAND; device.eraseBlockInNAND = nandemul_EraseBlockInNAND; @@ -321,6 +451,9 @@ int main(int argc,char *argv[]) printf("Testing on RAM emulation\n"); #endif + device.startBlock = 1; // Don't use block 0 + device.endBlock = device.nBlocks - 1; + yaffs_GutsInitialise(&device); // yaffs_GutsTest(); diff --git a/yaffsdev.proj b/yaffsdev.proj index e3e07f414dd3e0e9d25092f5de900db17f85a5be..baf39743ee41e861cc03c6a947a81e1cbc6d12d0 100644 GIT binary patch delta 2314 zcmbVNZA=?w9B22^4QxB=cpIbg3>3;J*Ue+*>*g}^tC<&3xw$wQ*OZVX3$slea&uk| z-g7qArIPbAlM3>39P)5p)Y9rktsNa?ox^KmYe!q7+uNG?*;!wEq{CEa`kTGS{>uKu z-ezyI=UA0h*lG3#^D%RYd7oKfmYJ8B1S2yqFcZumGr&-L3JNH^W}X>&K$&U_&WMvu zaL#`mQ0Z>GHqi4zIJ>ujC$PUFAe>2}QArTHBbq#I7|f}X0tx>(Hqw>S{*WLeDHKv9 z@)JjkCaKbixH2s%2!n=`{AMV9XK3|CV-gxs zG@0mU2^>>0dR9VeT#Q$va9oo`q{>a5F=w#N^#NVKxh9Zy- zdh&L*@p|~8p@eYZT0;fc#%p0=A`ZtYOptz_h17rz%i1B%v#>N^hCjybr6;?3d;R>t zus<-=J3Kbz-Q8mzDAN+e?Qfw(WK;9SI&`R+(GCMuFl zupTYMm_In$*v_m4&r%1}yMt5N9Gd$wUQJg(psOEFlz}D7(oQ;75D<$j#?SY?S$E?CLIf zmxuI(b=cZ$by*dN$d*1^`ZmF%DN z9r{Q5d-^;2EBXq3kzS?Wq2H!orqi@cPt#+xpYEo+XpVN#h(18?qiuAWveDmCQF?_s zLeEp>^aypIYM|Drhr2$2i>Ip@Q3?yNAk^UZb%4nyjuM6zQwzjNUYZNVG*K!T6sDyx zDRL24@0_X#Nmm5Pf-Lg5lUyOAKbj)AK5*kL*el@HOb_;V^w{-tQd?G~1Mg(%&_r<0J8b%&jilMHDq2X@6nIW8 zVYv1VFZ}K+JBrU`Sm&!q=+GiM+|scvxb4oCXPx`HL~&RllX;`}2uLaC^>(;84~J3) qL_lxK<@u7X9U+}OueEppt0623z<6bOj1?A{(jbYIk?4}zZ7}p!)VMRF@`?~)E^j4kRQY)ngB71fTA%#qDF>jG=`XYZa>Bbn%Few-1j-p zeb0NI_dV~OnnqL8=wJ`JpE|v<2^U}B+}s7OjC(QjJTB)-6_G@QWOEy~wNK!-*7Y55 ztf{f4Wjvk^k0`jKy9;-9;*QQX+}Yi&%_%xJc67HlwsmBFY^t;MoX{W9hxJ~)M?a{4 zO20{ehrP-Ez+Pva>;|@(t!7uVW|m=Rb-(DY>#piP*L|i#OBiN9oijUj{fW4d=9Eg5 zFf$$&g7B`_24{C;;CFZFZ8pX)LEqriWGEU9VnLD=5|3lALIeqGLo7RnXnf9J#b0`!Q>5-h_n3~R=OY_Sw!lA@lZ{9+Q!7W6j0rkp?nl5bh z=t7D6kjd5W8ua$L1_r&`ott|#?!(ZzXV#LOTB8Xn@whL6!}3I4Q-4B@w}`lzqy$mL zZMc<+r2UgoS;FJ?obbIl%^RBJh+02^8z@5|s*-n+PsL~gro82vuKIzPpv;Szx2OhX zTb{|;_q;`xf$+L?qlu)?mg~z_BOkbZDrT7YgnYai+)Dm zp>NS;bPkd^)P?NGhE}1K=s?j*^aYZ2)2K(6M&-I) z=pL#^7tvou$Kk~AYM9y8J2^MWWPcXsOXl$V+@{y5RltVPSIj|*9j4gQ9F2}wn3pJa zWD)i?lcLyBigEM2t4x$)$EdB#V{vqik%&)7gj7Zv@sgAng1?9NnIFOunNsl2U2Zy zc78#|Fx;?YX*PoV1)r;{T4d9Matk8_N>~igD;MHI;?6ez=5!wYFIOBHHmOl@a z2I`@5%nYQq624EafS2V8ErQ~0M9sf)ux^cU-f}^y-v+ILVq1WX`G@B`ook6d~Ix{ng T1|!6_Su;0X=TYX~q{;jbz%PwG diff --git a/yportenv.h b/yportenv.h index fc84fc5..94e72fa 100644 --- a/yportenv.h +++ b/yportenv.h @@ -1,56 +1,136 @@ -/* - * 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 - * - * 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 __PORTENV_H__ -#define __PORTENV_H__ - -#ifndef __KERNEL__ - -#include "stdlib.h" -#include "stdio.h" -#include "string.h" - -#define YPRINTF(x) printf x -#define YMALLOC(x) malloc(x) -#define YFREE(x) free(x) - - -#define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s)) -#define YALERT(s) YINFO(s) - -#else - -#include "linux/kernel.h" -#include "linux/mm.h" -#include "linux/string.h" -#include "linux/slab.h" - -#define YPRINTF(x) printk x -#define YMALLOC(x) kmalloc(x,GFP_KERNEL) -#define YFREE(x) kfree(x) - -#endif - - - -#define YINFO(s) YPRINTF((KERN_DEBUG __FILE__ " %d %s\n",__LINE__,s)) -#define YALERT(s) YINFO(s) - -#endif - - - - +/* + * 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 + * + * 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 __PORTENV_H__ +#define __PORTENV_H__ + + +#if defined WIN32 + +// Win32 +#include "stdlib.h" +#include "stdio.h" +#include "string.h" + +#define YMALLOC(x) malloc(x) +#define YFREE(x) free(x) + + +#define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s)) +#define YALERT(s) YINFO(s) + +#include + +#define YAFFS_LOSTNFOUND_NAME "LOST-N-FOUND" +#define YAFFS_LOSTNFOUND_PREFIX "OBJ" + +#define YPRINTF(x) + +// Always pass the sum compare to overcome the case insensitivity issue +#define yaffs_SumCompare(x,y) 1 +#define yaffs_strcmp(a,b) _stricmp(a,b) + + +#define u_char unsigned char +#define loff_t int +#define S_IFDIR 04000 + +#define S_ISFIFO(x) 0 +#define S_ISCHR(x) 0 +#define S_ISBLK(x) 0 +#define S_ISSOCK(x) 0 + +extern unsigned yfsd_U32FileTimeNow(void); + +#define CURRENT_TIME yfsd_U32FileTimeNow() +#define YAFFS_ROOT_MODE FILE_ATTRIBUTE_ARCHIVE + + +#define TENDSTR "\r\n" +#define TSTR(x) TEXT(x) +#define T(x) RETAILMSG(1, x) + + +#elif defined __KERNEL__ + + + +// Linux kernel +#include "linux/kernel.h" +#include "linux/mm.h" +#include "linux/string.h" +#include "linux/slab.h" +#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_SumCompare(x,y) ((x) == (y)) +#define yaffs_strcmp(a,b) strcmp(a,b) + +#define TENDSTR "\n" +#define TSTR(x) KERN_DEBUG x +#define T(x) printk x + + +#else + +// Linux application +#include "stdlib.h" +#include "stdio.h" +#include "string.h" + +#define YMALLOC(x) malloc(x) +#define YFREE(x) free(x) + + +#define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s)) +#define YALERT(s) YINFO(s) + + +#define TENDSTR "\n" +#define TSTR(x) x +#define T(x) printf x + + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj" +#define YPRINTF(x) printf x + +#define CURRENT_TIME 0 +#define YAFFS_ROOT_MODE 0666 + +#define yaffs_SumCompare(x,y) ((x) == (y)) +#define yaffs_strcmp(a,b) strcmp(a,b) + +#endif + + + +#undef YINFO + + +#define YINFO(s) YPRINTF((KERN_DEBUG __FILE__ " %d %s\n",__LINE__,s)) +#define YALERT(s) YINFO(s) + + + +#endif -- 2.30.2