diff options
author | Charles Manning <cdhmanning@gmail.com> | 2009-11-05 13:15:21 +1300 |
---|---|---|
committer | Arve Hjønnevåg <arve@android.com> | 2010-02-08 15:09:11 -0800 |
commit | 75e82381df19bcb8bf6681f3fe1a2a8360e6cddc (patch) | |
tree | 8e68a842012511e7786414d6473b88f48a6ee5cc /fs | |
parent | 8ff4860c2778c0be92a63808036de337d13fa864 (diff) |
Add handling for unrooted files
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/yaffs2/Kconfig | 8 | ||||
-rw-r--r-- | fs/yaffs2/yaffs_fs.c | 18 | ||||
-rw-r--r-- | fs/yaffs2/yaffs_guts.c | 123 | ||||
-rw-r--r-- | fs/yaffs2/yaffs_guts.h | 2 |
4 files changed, 150 insertions, 1 deletions
diff --git a/fs/yaffs2/Kconfig b/fs/yaffs2/Kconfig index c0e545b647b1..85844504057c 100644 --- a/fs/yaffs2/Kconfig +++ b/fs/yaffs2/Kconfig @@ -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. diff --git a/fs/yaffs2/yaffs_fs.c b/fs/yaffs2/yaffs_fs.c index 47cae6f4f92c..45aa158b18d3 100644 --- a/fs/yaffs2/yaffs_fs.c +++ b/fs/yaffs2/yaffs_fs.c @@ -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) { diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c index 05ff48d1bc46..a565b08e6116 100644 --- a/fs/yaffs2/yaffs_guts.c +++ b/fs/yaffs2/yaffs_guts.c @@ -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) { diff --git a/fs/yaffs2/yaffs_guts.h b/fs/yaffs2/yaffs_guts.h index a3b110291d38..acc2ae71c856 100644 --- a/fs/yaffs2/yaffs_guts.h +++ b/fs/yaffs2/yaffs_guts.h @@ -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 |