Add handling for unrooted files
authorCharles Manning <cdhmanning@gmail.com>
Thu, 5 Nov 2009 00:15:21 +0000 (13:15 +1300)
committerArve Hjønnevåg <arve@android.com>
Mon, 8 Feb 2010 23:09:11 +0000 (15:09 -0800)
Unrooted files should only happen due to a now fixed bug that
would rmdir non-empty directories.
Unrooted files are now re-rooted in lost_found.

This also introduces a mechanism to empty out lost and found at
mount, thus recaliming this space. This option may be controlled
via a compile flag and overridden with a mount option.

Signed-off-by: Charles Manning <cdhmanning@gmail.com>
fs/yaffs2/Kconfig
fs/yaffs2/yaffs_fs.c
fs/yaffs2/yaffs_guts.c
fs/yaffs2/yaffs_guts.h

index c0e545b647b1c9e882dda2fdb9de89bb3737bdf2..85844504057cd989e00ec373860dd72aa13bdc40 100644 (file)
@@ -154,3 +154,11 @@ 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 mount"
+       depends on YAFFS_FS
+       default n
+       help
+         If this is enabled then the contents of lost and found is
+         automatically dumped at mount.
index 47cae6f4f92cb7460889ce9ab8f0a205d9695c27..45aa158b18d330196a7cf66cfd5119cd9926e9cf 100644 (file)
@@ -1796,6 +1796,8 @@ typedef struct {
        int skip_checkpoint_read;
        int skip_checkpoint_write;
        int no_cache;
+       int empty_lost_and_found_overridden;
+       int empty_lost_and_found;
 } yaffs_options;
 
 #define MAX_OPT_LEN 20
@@ -1811,6 +1813,9 @@ static int yaffs_parse_options(yaffs_options *options, const char *options_str)
                memset(cur_opt, 0, MAX_OPT_LEN + 1);
                p = 0;
 
+               while (*options_str == ',')
+                       options_str++;
+
                while (*options_str && *options_str != ',') {
                        if (p < MAX_OPT_LEN) {
                                cur_opt[p] = *options_str;
@@ -1830,6 +1835,12 @@ static int yaffs_parse_options(yaffs_options *options, const char *options_str)
                else if (!strcmp(cur_opt, "no-checkpoint")) {
                        options->skip_checkpoint_read = 1;
                        options->skip_checkpoint_write = 1;
+               } else if (!strcmp(cur_opt, "empty-lost-and-found-disable")) {
+                       options->empty_lost_and_found = 0;
+                       options->empty_lost_and_found_overridden = 1;
+               } else if (!strcmp(cur_opt, "empty-lost-and-found-enable")) {
+                       options->empty_lost_and_found = 1;
+                       options->empty_lost_and_found_overridden = 1;
                } else {
                        printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n",
                                        cur_opt);
@@ -1935,6 +1946,13 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion,
        T(YAFFS_TRACE_OS, (" size %lld\n", mtd->size));
 #endif
 
+
+#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;
+
 #ifdef CONFIG_YAFFS_AUTO_YAFFS2
 
        if (yaffsVersion == 1 && WRITE_SIZE(mtd) >= 2048) {
index 05ff48d1bc468e0ef2b582dcd0f796bd937be673..a565b08e6116108d401263d6ec5421d70c85ab48 100644 (file)
@@ -113,7 +113,6 @@ static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,
                                        yaffs_FileStructure *fStruct,
                                        __u32 chunkId);
 
-
 /* Function to calculate chunk and offset */
 
 static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, int *chunkOut,
@@ -5450,6 +5449,125 @@ static void yaffs_StripDeletedObjects(yaffs_Device *dev)
 
 }
 
+/*
+ * 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;
@@ -7413,6 +7531,9 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
                                init_failed = 1;
 
                yaffs_StripDeletedObjects(dev);
+               yaffs_FixHangingObjects(dev);
+               if(dev->emptyLostAndFound)
+                       yaffs_EmptyLostAndFound(dev);
        }
 
        if (init_failed) {
index a3b110291d3832c4b73a83069e9e7d3d1fc7b92f..acc2ae71c8566a362c05f5a6e300c0ba40a19d6a 100644 (file)
@@ -557,6 +557,8 @@ struct yaffs_DeviceStruct {
 
        int useHeaderFileSize;  /* Flag to determine if we should use file sizes from the header */
 
+       int emptyLostAndFound;  /* Flasg to determine if lst+found should be emptied on init */
+
        int useNANDECC;         /* Flag to decide whether or not to use NANDECC */
 
        void *genericDevice;    /* Pointer to device context