changeset 311:97a4bd3d225e

Add fix for hanging objects
author charles <charles>
date Sat, 07 Nov 2009 02:06:58 +0000
parents 49eaa5873a0e
children b5d302efa7dd
files Kconfig moduleconfig.h yaffs_fs.c yaffs_guts.c yaffs_guts.h
diffstat 5 files changed, 214 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/Kconfig
+++ b/Kconfig
@@ -166,3 +166,12 @@ config YAFFS_SHORT_NAMES_IN_RAM
 	  but makes look-ups faster.
 
 	  If unsure, say Y.
+
+config YAFFS_EMPTY_LOST_AND_FOUND
+	bool "Empty lost and found on boot"
+	depends on YAFFS_FS
+	default n
+	help
+	  If this is enabled then the contents of lost and found is
+	  automatically dumped at mount.
+
--- a/moduleconfig.h
+++ b/moduleconfig.h
@@ -42,6 +42,12 @@
 /* Meaning: Disables testing whether chunks are erased before writing to them*/
 #define CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
 
+/* Default: Not Selected */
+/* Meaning: At mount automatically empty all files from lost and found. */
+/* This is done to fix an old problem where rmdir was not checking for an */
+/* empty directory. This can also be achieved with a mount option. */
+/* #define CONFIG_YAFFS_EMPTY_LOST_AND_FOUND */
+
 /* Default: Selected */
 /* Meaning: Cache short names, taking more RAM, but faster look-ups */
 #define CONFIG_YAFFS_SHORT_NAMES_IN_RAM
--- a/yaffs_fs.c
+++ b/yaffs_fs.c
@@ -32,7 +32,7 @@
  */
 
 const char *yaffs_fs_c_version =
-    "$Id: yaffs_fs.c,v 1.85 2009-10-15 00:45:46 charles Exp $";
+    "$Id: yaffs_fs.c,v 1.86 2009-11-07 02:11:01 charles Exp $";
 extern const char *yaffs_guts_c_version;
 
 #include <linux/version.h>
@@ -118,7 +118,7 @@ static uint32_t YCALCBLOCKS(uint64_t par
 #include "yaffs_mtdif1.h"
 #include "yaffs_mtdif2.h"
 
-unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS;
+unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS | YAFFS_TRACE_ALWAYS;
 unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS;
 unsigned int yaffs_auto_checkpoint = 1;
 
@@ -1814,11 +1814,13 @@ typedef struct {
 	int no_cache;
 	int tags_ecc_on;
 	int tags_ecc_overridden;
-	int lazy_load_enabled;
-	int lazy_load_overridden;
+	int lazy_loading_enabled;
+	int lazy_loading_overridden;
+	int empty_lost_and_found;
+	int empty_lost_and_found_overridden;
 } yaffs_options;
 
-#define MAX_OPT_LEN 20
+#define MAX_OPT_LEN 30
 static int yaffs_parse_options(yaffs_options *options, const char *options_str)
 {
 	char cur_opt[MAX_OPT_LEN + 1];
@@ -1850,12 +1852,18 @@ static int yaffs_parse_options(yaffs_opt
 		} else if (!strcmp(cur_opt, "tags-ecc-on")){
 			options->tags_ecc_on = 1;
 			options->tags_ecc_overridden = 1;
-		} else if (!strcmp(cur_opt, "lazy-load-off")){
-			options->lazy_load_enabled = 0;
-			options->lazy_load_overridden=1;
-		} else if (!strcmp(cur_opt, "lazy-load-on")){
-			options->lazy_load_enabled = 1;
-			options->lazy_load_overridden = 1;
+		} else if (!strcmp(cur_opt, "lazy-loading-off")){
+			options->lazy_loading_enabled = 0;
+			options->lazy_loading_overridden=1;
+		} else if (!strcmp(cur_opt, "lazy-loading-on")){
+			options->lazy_loading_enabled = 1;
+			options->lazy_loading_overridden = 1;
+		} else if (!strcmp(cur_opt, "empty-lost-and-found-off")){
+			options->empty_lost_and_found = 0;
+			options->empty_lost_and_found_overridden=1;
+		} else if (!strcmp(cur_opt, "empty-lost-and-found-on")){
+			options->empty_lost_and_found = 1;
+			options->empty_lost_and_found_overridden=1;
 		} else if (!strcmp(cur_opt, "no-cache"))
 			options->no_cache = 1;
 		else if (!strcmp(cur_opt, "no-checkpoint-read"))
@@ -2075,8 +2083,8 @@ static struct super_block *yaffs_interna
 #ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD
 	dev->disableLazyLoad = 1;
 #endif
-	if(options.lazy_load_overridden)
-		dev->disableLazyLoad = !options.lazy_load_enabled;
+	if(options.lazy_loading_overridden)
+		dev->disableLazyLoad = !options.lazy_loading_enabled;
 
 #ifdef CONFIG_YAFFS_DISABLE_TAGS_ECC
 	dev->noTagsECC = 1;
@@ -2084,6 +2092,12 @@ static struct super_block *yaffs_interna
 	if(options.tags_ecc_overridden)
 		dev->noTagsECC = !options.tags_ecc_on;
 
+#ifdef CONFIG_YAFFS_EMPTY_LOST_AND_FOUND
+	dev->emptyLostAndFound = 1;
+#endif
+	if(options.empty_lost_and_found_overridden)
+		dev->emptyLostAndFound = options.empty_lost_and_found;
+
 	/* ... and the functions. */
 	if (yaffsVersion == 2) {
 		dev->writeChunkWithTagsToNAND =
@@ -2284,7 +2298,7 @@ static DECLARE_FSTYPE(yaffs2_fs_type, "y
 
 static struct proc_dir_entry *my_proc_entry;
 
-static char *yaffs_dump_dev(char *buf, yaffs_Device * dev)
+static char *yaffs_dump_dev_part0(char *buf, yaffs_Device * dev)
 {
 	buf += sprintf(buf, "startBlock......... %d\n", dev->startBlock);
 	buf += sprintf(buf, "endBlock........... %d\n", dev->endBlock);
@@ -2319,10 +2333,19 @@ static char *yaffs_dump_dev(char *buf, y
 	buf += sprintf(buf, "nUnlinkedFiles..... %d\n", dev->nUnlinkedFiles);
 	buf +=
 	    sprintf(buf, "nBackgroudDeletions %d\n", dev->nBackgroundDeletions);
+
+	return buf;
+}
+
+
+static char *yaffs_dump_dev_part1(char *buf, yaffs_Device * dev)
+{
 	buf += sprintf(buf, "useNANDECC......... %d\n", dev->useNANDECC);
 	buf += sprintf(buf, "noTagsECC.......... %d\n", dev->noTagsECC);
 	buf += sprintf(buf, "isYaffs2........... %d\n", dev->isYaffs2);
 	buf += sprintf(buf, "inbandTags......... %d\n", dev->inbandTags);
+	buf += sprintf(buf, "emptyLostAndFound.. %d\n", dev->emptyLostAndFound);
+	buf += sprintf(buf, "disableLazyLoad.... %d\n", dev->disableLazyLoad);
 
 	return buf;
 }
@@ -2345,28 +2368,36 @@ static int yaffs_proc_read(char *page,
 	*(int *)start = 1;
 
 	/* Print header first */
-	if (step == 0) {
+	if (step == 0)
 		buf += sprintf(buf, "YAFFS built:" __DATE__ " " __TIME__
 			       "\n%s\n%s\n", yaffs_fs_c_version,
 			       yaffs_guts_c_version);
+	else if (step == 1)
+		buf += sprintf(buf,"\n");
+	else {
+		step-=2;
+		
+		/* hold lock_kernel while traversing yaffs_dev_list */
+		lock_kernel();
+
+		/* Locate and print the Nth entry.  Order N-squared but N is small. */
+		ylist_for_each(item, &yaffs_dev_list) {
+			yaffs_Device *dev = ylist_entry(item, yaffs_Device, devList);
+			if (n < (step & ~1)) {
+				n+=2;
+				continue;
+			}
+			if((step & 1)==0){
+				buf += sprintf(buf, "\nDevice %d \"%s\"\n", n, dev->name);
+				buf = yaffs_dump_dev_part0(buf, dev);
+			} else
+				buf = yaffs_dump_dev_part1(buf, dev);
+			
+			break;
+		}
+		unlock_kernel();
 	}
 
-	/* hold lock_kernel while traversing yaffs_dev_list */
-	lock_kernel();
-
-	/* Locate and print the Nth entry.  Order N-squared but N is small. */
-	ylist_for_each(item, &yaffs_dev_list) {
-		yaffs_Device *dev = ylist_entry(item, yaffs_Device, devList);
-		if (n < step) {
-			n++;
-			continue;
-		}
-		buf += sprintf(buf, "\nDevice %d \"%s\"\n", n, dev->name);
-		buf = yaffs_dump_dev(buf, dev);
-		break;
-	}
-	unlock_kernel();
-
 	return buf - page < count ? buf - page : count;
 }
 
@@ -2410,7 +2441,7 @@ static struct {
 };
 
 #define MAX_MASK_NAME_LENGTH 40
-static int yaffs_proc_write(struct file *file, const char *buf,
+static int yaffs_proc_write_trace_options(struct file *file, const char *buf,
 					 unsigned long count, void *data)
 {
 	unsigned rg = 0, mask_bitfield;
@@ -2502,6 +2533,13 @@ static int yaffs_proc_write(struct file 
 	return count;
 }
 
+
+static int yaffs_proc_write(struct file *file, const char *buf,
+					 unsigned long count, void *data)
+{
+        return yaffs_proc_write_trace_options(file, buf, count, data);
+}
+
 /* Stuff to handle installation of file systems */
 struct file_system_to_install {
 	struct file_system_type *fst;
--- a/yaffs_guts.c
+++ b/yaffs_guts.c
@@ -12,7 +12,7 @@
  */
 
 const char *yaffs_guts_c_version =
-    "$Id: yaffs_guts.c,v 1.92 2009-11-03 02:41:00 charles Exp $";
+    "$Id: yaffs_guts.c,v 1.93 2009-11-07 02:06:58 charles Exp $";
 
 #include "yportenv.h"
 
@@ -113,7 +113,6 @@ static yaffs_Tnode *yaffs_FindLevel0Tnod
 					yaffs_FileStructure *fStruct,
 					__u32 chunkId);
 
-
 /* Function to calculate chunk and offset */
 
 static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, int *chunkOut,
@@ -5471,6 +5470,125 @@ static void yaffs_StripDeletedObjects(ya
 
 }
 
+/*
+ * This code iterates through all the objects making sure that they are rooted.
+ * Any unrooted objects are re-rooted in lost+found.
+ * An object needs to be in one of:
+ * - Directly under deleted, unlinked
+ * - Directly or indirectly under root.
+ *
+ * Note:
+ *  This code assumes that we don't ever change the current relationships between
+ *  directories:
+ *   rootDir->parent == unlinkedDir->parent == deletedDir->parent == NULL
+ *   lostNfound->parent == rootDir
+ *
+ * This fixes the problem where directories might have inadvertently been deleted
+ * leaving the object "hanging" without being rooted in the directory tree.
+ */
+ 
+static int yaffs_HasNULLParent(yaffs_Device *dev, yaffs_Object *obj)
+{
+	return (obj == dev->deletedDir ||
+		obj == dev->unlinkedDir||
+		obj == dev->rootDir);
+}
+
+static void yaffs_FixHangingObjects(yaffs_Device *dev)
+{
+	yaffs_Object *obj;
+	yaffs_Object *parent;
+	int i;
+	struct ylist_head *lh;
+	struct ylist_head *n;
+	int depthLimit;
+	int hanging;
+
+
+	/* Iterate through the objects in each hash entry,
+	 * looking at each object.
+	 * Make sure it is rooted.
+	 */
+
+	for (i = 0; i <  YAFFS_NOBJECT_BUCKETS; i++) {
+		ylist_for_each_safe(lh, n, &dev->objectBucket[i].list) {
+			if (lh) {
+				obj = ylist_entry(lh, yaffs_Object, hashLink);
+				parent= obj->parent;
+				
+				if(yaffs_HasNULLParent(dev,obj)){
+					/* These directories are not hanging */
+					hanging = 0;
+				}
+				else if(!parent || parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+					hanging = 1;
+				else if(yaffs_HasNULLParent(dev,parent))
+					hanging = 0;
+				else {
+					/*
+					 * Need to follow the parent chain to see if it is hanging.
+					 */
+					hanging = 0;
+					depthLimit=100;
+
+					while(parent != dev->rootDir &&
+						parent->parent &&
+						parent->parent->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
+						depthLimit > 0){
+						parent = parent->parent;
+						depthLimit--;
+					}
+					if(parent != dev->rootDir)
+						hanging = 1;
+				}
+				if(hanging){
+					T(YAFFS_TRACE_SCAN,
+					  (TSTR("Hanging object %d moved to lost and found" TENDSTR),
+					  	obj->objectId));
+					yaffs_AddObjectToDirectory(dev->lostNFoundDir,obj);
+				}
+			}
+		}
+	}
+}
+
+
+/*
+ * Delete directory contents for cleaning up lost and found.
+ */
+static void yaffs_DeleteDirectoryContents(yaffs_Object *dir)
+{
+	yaffs_Object *obj;
+	struct ylist_head *lh;
+	struct ylist_head *n;
+
+	if(dir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+		YBUG();
+	
+	ylist_for_each_safe(lh, n, &dir->variant.directoryVariant.children) {
+		if (lh) {
+			obj = ylist_entry(lh, yaffs_Object, siblings);
+			if(obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
+				yaffs_DeleteDirectoryContents(obj);
+
+			T(YAFFS_TRACE_SCAN,
+				(TSTR("Deleting lost_found object %d" TENDSTR),
+				obj->objectId));
+
+			/* Need to use UnlinkObject since Delete would not handle
+			 * hardlinked objects correctly.
+			 */
+			yaffs_UnlinkObject(obj); 
+		}
+	}
+			
+}
+
+static void yaffs_EmptyLostAndFound(yaffs_Device *dev)
+{
+	yaffs_DeleteDirectoryContents(dev->lostNFoundDir);
+}
+
 static int yaffs_Scan(yaffs_Device *dev)
 {
 	yaffs_ExtendedTags tags;
@@ -7432,6 +7550,9 @@ int yaffs_GutsInitialise(yaffs_Device *d
 				init_failed = 1;
 
 		yaffs_StripDeletedObjects(dev);
+		yaffs_FixHangingObjects(dev);
+		if(dev->emptyLostAndFound)
+			yaffs_EmptyLostAndFound(dev);
 	}
 
 	if (init_failed) {
@@ -7455,7 +7576,6 @@ int yaffs_GutsInitialise(yaffs_Device *d
 	yaffs_VerifyFreeChunks(dev);
 	yaffs_VerifyBlocks(dev);
 
-
 	/* Clean up any aborted checkpoint data */
 	if(!dev->isCheckpointed && dev->blocksInCheckpoint > 0)
 		yaffs_InvalidateCheckpoint(dev);
--- a/yaffs_guts.h
+++ b/yaffs_guts.h
@@ -405,6 +405,8 @@ typedef union {
 	yaffs_HardLinkStructure hardLinkVariant;
 } yaffs_ObjectVariant;
 
+
+
 struct yaffs_ObjectStruct {
 	__u8 deleted:1;		/* This should only apply to unlinked files. */
 	__u8 softDeleted:1;	/* it has also been soft deleted */
@@ -532,6 +534,7 @@ typedef struct {
 
 /*----------------- Device ---------------------------------*/
 
+
 struct yaffs_DeviceStruct {
 	struct ylist_head devList;
 	const char *name;
@@ -768,7 +771,9 @@ struct yaffs_DeviceStruct {
 	/* yaffs2 runtime stuff */
 	unsigned sequenceNumber;	/* Sequence number of currently allocating block */
 	unsigned oldestDirtySequence;
-
+	
+	/* Auto empty lost and found directory on mount */
+	int emptyLostAndFound;
 };
 
 typedef struct yaffs_DeviceStruct yaffs_Device;