Skip to contentSkip to conversation

Hard lock when mounting loopback file

DISCUSSION Kyle 5 participants 8 messages Jan 11, 2002 – Jan 14, 2002 VFAT/FAT/msdos filesystem

lore
Share & tools
lore

Kyle

author
#0 lore raw

I have a digital camera flash card that is locking up my machine (stock redhat 7.2 w/2.4.9-13 kernel).

I can mount the card, but as soon as I browse the filesystem, the machine locks hard. I successfully copied the file system from the raw device to a file and tried mounting it as:

mount -o loop flash.img /mnt/flash

and it still locks up the machine just as before. This makes me think it has nothing to do with the USB reader or the SCSI emulation, etc.

My guess is I have a corrupt filesystem on the flash that the filesystem handler (vfat) is intolerant of (all my other flash cards work fine).

This seems like a possible kernel bug to me. I'm not much of a kernel expert but I have a copy of the offending image if anyone wants to or can look at it. (ftp://actarg.com/pub/misc/flash.img) Is there someone that knows how to figure out if the driver can spit out a harmless message about filesystem corruption rather than taking the whole kernel down?

Kyle Bateman

#1 lore raw
Quoted 16 lines · Kyle

I have a digital camera flash card that is locking up my machine (stock redhat 7.2 w/2.4.9-13 kernel).

I can mount the card, but as soon as I browse the filesystem, the machine locks hard. I successfully copied the file system from the raw device to a file and tried mounting it as:

mount -o loop flash.img /mnt/flash

and it still locks up the machine just as before. This makes me think it has nothing to do with the USB reader or the SCSI emulation, etc.

My guess is I have a corrupt filesystem on the flash that the filesystem handler (vfat) is intolerant of (all my other flash cards work fine).

Quoted 7 lines

This seems like a possible kernel bug to me. I'm not much of a kernel expert but I have a copy of the offending image if anyone wants to or can look at it. (ftp://actarg.com/pub/misc/flash.img) Is there someone that knows how to figure out if the driver can spit out a harmless message about filesystem corruption rather than taking the whole kernel down?

I don't know a thing about fat layout, but it appears that it uses a linked list of blocks, and if that list ends up pointing back onto itself, the kernel goes into an infinite loop in several places chasing its way to the end of the list.

The below patch fixed it for me, and I was able to mount and read your filesystem image.

Unless someone has a smarter fix, I'll send this to the kernel maintainers in a week or two.

fs/fat/misc.c +7 -0
@@ -478,6 +478,8 @@ static int raw_scan_nonroot(struct super
 	printk("raw_scan_nonroot: start=%d\n",start);
 #endif
 	do {
+		int old_start = start;
+
 		for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
 			if ((cluster = raw_scan_sector(sb,(start-2)*
 			    MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+
@@ -486,6 +488,11 @@ static int raw_scan_nonroot(struct super
 		}
 		if (!(start = fat_access(sb,start,-1))) {
 			fat_fs_panic(sb,"FAT error");
+			break;
+		}
+		if (start == old_start) {
+			/* Prevent infinite loop on corrupt fs */
+			fat_fs_panic(sb, "FAT loop");
 			break;
 		}
 #ifdef DEBUG
fs/fat/inode.c +12 -0
@@ -392,12 +392,18 @@ static void fat_read_root(struct inode *
 		MSDOS_I(inode)->i_start = sbi->root_cluster;
 		if ((nr = MSDOS_I(inode)->i_start) != 0) {
 			while (nr != -1) {
+				int old_nr = nr;
 				inode->i_size += 1 << sbi->cluster_bits;
 				if (!(nr = fat_access(sb, nr, -1))) {
 					printk("Directory %ld: bad FAT\n",
 					       inode->i_ino);
 					break;
 				}
+				if (nr == old_nr) {
+					printk("Directory %ld: FAT loop\n",
+					       inode->i_ino);
+					break;
+				}
 			}
 		}
 	} else {
@@ -918,9 +924,15 @@ static void fat_fill_inode(struct inode 
 #endif
 		if ((nr = MSDOS_I(inode)->i_start) != 0)
 			while (nr != -1) {
+				int old_nr = nr;
 				inode->i_size += 1 << sbi->cluster_bits;
 				if (!(nr = fat_access(sb, nr, -1))) {
 					printk("Directory %ld: bad FAT\n",
+					    inode->i_ino);
+					break;
+				}
+				if (nr == old_nr) {
+					printk("Directory %ld: FAT loop\n",
 					    inode->i_ino);
 					break;
 				}
#2 lore raw

Andrew Morton <[email protected]> said:

[...]

Quoted 4 lines

I don't know a thing about fat layout, but it appears that it uses a linked list of blocks, and if that list ends up pointing back onto itself, the kernel goes into an infinite loop in several places chasing its way to the end of the list.

Right.

Quoted 2 lines

The below patch fixed it for me, and I was able to mount and read your filesystem image.

AFAIKS, this just cures the case where the wild pointer points to the first block of the offending file. You should perhaps count the blocks you go through, so a file something like:

-------+-----+

        ^     |
        |     |
        +-----+

or other strange pointer messups don't get you.

BTW, is fsck (the Linux or the Win32 variety) able to fix this?

Signature · 1 line
Horst von Brand			     http://counter.li.org # 22616
#3 lore raw
In reply to Kyle
Quoted 10 lines · Andrew Morton

I don't know a thing about fat layout, but it appears that it uses a linked list of blocks, and if that list ends up pointing back onto itself, the kernel goes into an infinite loop in several places chasing its way to the end of the list.

The below patch fixed it for me, and I was able to mount and read your filesystem image.

Unless someone has a smarter fix, I'll send this to the kernel maintainers in a week or two.

It seems to me that this patch will find only those infinite loops where the last link of the chain points to itself. But there could be loops where the last link points to the middle of the chain.

Additional check on the number of followed links could be useful there. No chain should be longer than the number of clusters on the fs. Although on large FAT32 filesystems the number of clusters can be high, a very long loop is still better than an infinite one. (In cases where we know the file size, this limit can be reduced to file_size/cluster_size + 1 links).

Marius Gedminas

Signature · 2 lines
This company has performed an illegal operation and will be shut down. If
the problem persists, contact your vendor or appeal to a higher court.
#4 lore raw
In reply to Kyle
Quoted 10 lines · Marius Gedminas
Quoted 8 lines · Andrew Morton

I don't know a thing about fat layout, but it appears that it uses a linked list of blocks, and if that list ends up pointing back onto itself, the kernel goes into an infinite loop in several places chasing its way to the end of the list.

The below patch fixed it for me, and I was able to mount and read your filesystem image.

Quoted 6 lines
Quoted 2 lines

Unless someone has a smarter fix, I'll send this to the kernel maintainers in a week or two.

It seems to me that this patch will find only those infinite loops where the last link of the chain points to itself. But there could be loops where the last link points to the middle of the chain.

Agree.

Quoted 6 lines

Additional check on the number of followed links could be useful there. No chain should be longer than the number of clusters on the fs. Although on large FAT32 filesystems the number of clusters can be high, a very long loop is still better than an infinite one. (In cases where we know the file size, this limit can be reduced to file_size/cluster_size + 1 links).

hmm.. OK, I'll take a look at that approach.

-

maintainer
#5 lore raw
In reply to Kyle
Quoted 8 lines · Andrew Morton
Quoted 6 lines

Additional check on the number of followed links could be useful there. No chain should be longer than the number of clusters on the fs. Although on large FAT32 filesystems the number of clusters can be high, a very long loop is still better than an infinite one. (In cases where we know the file size, this limit can be reduced to file_size/cluster_size + 1 links).

hmm.. OK, I'll take a look at that approach.

The maximum number of directory entries is 65536 in ordinarily. The following patch prevents the infinite loop of a directory (include the limit to added directory).

I think the following patch is a bit useful.

Signature · 213 lines
OGAWA Hirofumi <[email protected]>

diff -urN linux-2.5.2-pre11/fs/fat/dir.c fat_loop/fs/fat/dir.c
--- linux-2.5.2-pre11/fs/fat/dir.c	Sat Oct 13 05:48:42 2001
+++ fat_loop/fs/fat/dir.c	Mon Jan 14 17:40:48 2002
@@ -727,7 +727,13 @@
 	offset = curr = 0;
 	*bh = NULL;
 	row = 0;
-	while (fat_get_entry(dir,&curr,bh,de,ino) > -1) {
+	while (fat_get_entry(dir, &curr, bh, de, ino) > -1) {
+		/* check the maximum size of directory */
+		if (curr >= FAT_MAX_DIR_SIZE) {
+			fat_brelse(sb, *bh);
+			return -ENOSPC;
+		}
+
 		if (IS_FREE((*de)->name)) {
 			if (++row == slots)
 				return offset;
@@ -742,7 +748,10 @@
 	if (!new_bh)
 		return -ENOSPC;
 	fat_brelse(sb, new_bh);
-	do fat_get_entry(dir,&curr,bh,de,ino); while (++row<slots);
+	do {
+		fat_get_entry(dir, &curr, bh, de, ino);
+	} while (++row < slots);
+
 	return offset;
 }
 
diff -urN linux-2.5.2-pre11/fs/fat/inode.c fat_loop/fs/fat/inode.c
--- linux-2.5.2-pre11/fs/fat/inode.c	Mon Jan 14 18:35:34 2002
+++ fat_loop/fs/fat/inode.c	Mon Jan 14 03:45:33 2002
@@ -373,11 +373,37 @@
 	return ret;
 }
 
+static void fat_calc_dir_size(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	int nr;
+
+	inode->i_size = 0;
+	if (MSDOS_I(inode)->i_start == 0)
+		return;
+
+	nr = MSDOS_I(inode)->i_start;
+	do {
+		inode->i_size += 1 << MSDOS_SB(sb)->cluster_bits;
+		if (!(nr = fat_access(sb, nr, -1))) {
+			printk("FAT: Directory %ld: bad FAT\n",
+			       inode->i_ino);
+			break;
+		}
+		if (inode->i_size > FAT_MAX_DIR_SIZE) {
+			fat_fs_panic(sb, "Directory %ld: "
+				     "exceeded the maximum size of directory",
+				     inode->i_ino);
+			inode->i_size = FAT_MAX_DIR_SIZE;
+			break;
+		}
+	} while (nr != -1);
+}
+
 static void fat_read_root(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
-	int nr;
 
 	INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
 	MSDOS_I(inode)->i_location = 0;
@@ -391,16 +417,7 @@
 	inode->i_fop = &fat_dir_operations;
 	if (sbi->fat_bits == 32) {
 		MSDOS_I(inode)->i_start = sbi->root_cluster;
-		if ((nr = MSDOS_I(inode)->i_start) != 0) {
-			while (nr != -1) {
-				inode->i_size += 1 << sbi->cluster_bits;
-				if (!(nr = fat_access(sb, nr, -1))) {
-					printk("Directory %ld: bad FAT\n",
-					       inode->i_ino);
-					break;
-				}
-			}
-		}
+		fat_calc_dir_size(inode);
 	} else {
 		MSDOS_I(inode)->i_start = 0;
 		inode->i_size = sbi->dir_entries * sizeof(struct msdos_dir_entry);
@@ -882,7 +899,6 @@
 {
 	struct super_block *sb = inode->i_sb;
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
-	int nr;
 
 	INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
 	MSDOS_I(inode)->i_location = 0;
@@ -913,15 +929,7 @@
 			inode->i_nlink = 1;
 		}
 #endif
-		if ((nr = MSDOS_I(inode)->i_start) != 0)
-			while (nr != -1) {
-				inode->i_size += 1 << sbi->cluster_bits;
-				if (!(nr = fat_access(sb, nr, -1))) {
-					printk("Directory %ld: bad FAT\n",
-					    inode->i_ino);
-					break;
-				}
-			}
+		fat_calc_dir_size(inode);
 		MSDOS_I(inode)->mmu_private = inode->i_size;
 	} else { /* not a directory */
 		inode->i_generation |= 1;
diff -urN linux-2.5.2-pre11/fs/fat/misc.c fat_loop/fs/fat/misc.c
--- linux-2.5.2-pre11/fs/fat/misc.c	Mon Jan 14 18:35:34 2002
+++ fat_loop/fs/fat/misc.c	Mon Jan 14 06:13:24 2002
@@ -38,15 +38,25 @@
  * read-only. The file system can be made writable again by remounting it.
  */
 
-void fat_fs_panic(struct super_block *s,const char *msg)
+static char panic_msg[512];
+
+void fat_fs_panic(struct super_block *s, const char *fmt, ...)
 {
 	int not_ro;
+	va_list args;
+
+	va_start (args, fmt);
+	vsnprintf (panic_msg, sizeof(panic_msg), fmt, args);
+	va_end (args);
 
 	not_ro = !(s->s_flags & MS_RDONLY);
-	if (not_ro) s->s_flags |= MS_RDONLY;
-	printk("Filesystem panic (dev %s).\n  %s\n", s->s_id, msg);
 	if (not_ro)
-		printk("  File system has been set read-only\n");
+		s->s_flags |= MS_RDONLY;
+
+	printk("FAT: Filesystem panic (dev %s)\n"
+	       "    %s\n", s->s_id, panic_msg);
+	if (not_ro)
+		printk("    File system has been set read-only\n");
 }
 
 
@@ -472,11 +482,13 @@
     int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry
     **res_de)
 {
-	int count,cluster;
+	int count, cluster;
+	unsigned long dir_size;
 
 #ifdef DEBUG
 	printk("raw_scan_nonroot: start=%d\n",start);
 #endif
+	dir_size = 0;
 	do {
 		for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
 			if ((cluster = raw_scan_sector(sb,(start-2)*
@@ -484,6 +496,13 @@
 			    count,name,number,ino,res_bh,res_de)) >= 0)
 				return cluster;
 		}
+		dir_size += 1 << MSDOS_SB(sb)->cluster_bits;
+		if (dir_size > FAT_MAX_DIR_SIZE) {
+			fat_fs_panic(sb, "Directory %d: "
+				     "exceeded the maximum size of directory",
+				     start);
+			break;
+		}
 		if (!(start = fat_access(sb,start,-1))) {
 			fat_fs_panic(sb,"FAT error");
 			break;
@@ -491,8 +510,8 @@
 #ifdef DEBUG
 	printk("next start: %d\n",start);
 #endif
-	}
-	while (start != -1);
+	} while (start != -1);
+
 	return -ENOENT;
 }
 
diff -urN linux-2.5.2-pre11/include/linux/msdos_fs.h fat_loop/include/linux/msdos_fs.h
--- linux-2.5.2-pre11/include/linux/msdos_fs.h	Mon Jan 14 18:35:41 2002
+++ fat_loop/include/linux/msdos_fs.h	Mon Jan 14 05:33:06 2002
@@ -19,6 +19,10 @@
 #define MSDOS_DPS_BITS	4 /* log2(MSDOS_DPS) */
 #define MSDOS_DIR_BITS	5 /* log2(sizeof(struct msdos_dir_entry)) */
 
+/* directory limit */
+#define FAT_MAX_DIR_ENTRIES	(65536)
+#define FAT_MAX_DIR_SIZE	(FAT_MAX_DIR_ENTRIES << MSDOS_DIR_BITS)
+
 #define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
 
 #define FAT_CACHE    8 /* FAT cache size */
@@ -297,7 +301,7 @@
 extern int fat_notify_change(struct dentry * dentry, struct iattr * attr);
 
 /* fat/misc.c */
-extern void fat_fs_panic(struct super_block *s, const char *msg);
+extern void fat_fs_panic(struct super_block *s, const char *fmt, ...);
 extern int fat_is_binary(char conversion, char *extension);
 extern void lock_fat(struct super_block *sb);
 extern void unlock_fat(struct super_block *sb);
#6 lore raw
In reply to Kyle
Quoted 10 lines · OGAWA Hirofumi
Quoted 7 lines · Andrew Morton
Quoted 6 lines

Additional check on the number of followed links could be useful there. No chain should be longer than the number of clusters on the fs. Although on large FAT32 filesystems the number of clusters can be high, a very long loop is still better than an infinite one. (In cases where we know the file size, this limit can be reduced to file_size/cluster_size + 1 links).

Quoted 7 lines
Quoted 1 line

hmm.. OK, I'll take a look at that approach.

The maximum number of directory entries is 65536 in ordinarily. The following patch prevents the infinite loop of a directory (include the limit to added directory).

I think the following patch is a bit useful.

Ah lovely. I was looking for your email address when this came up, but couldn't find it.

Here's the 2.4.18-pre3 version of your patch. I've tested it against the corrupted filesystem image and it works fine:

Jan 14 02:40:50 mnm kernel: FAT: Filesystem panic (dev 07:00)
Jan 14 02:40:50 mnm kernel:     Directory 3329: exceeded the maximum size of directory
Jan 14 02:40:50 mnm kernel:     File system has been set read-only
Jan 14 02:40:50 mnm kernel: FAT: Filesystem panic (dev 07:00)
Jan 14 02:40:50 mnm kernel:     Directory 5: exceeded the maximum size of directory

If you're satisfied that everything is OK, I'd suggest that you send the diff to Marcelo.

Thanks.

fs/fat/dir.c +11 -2
@@ -727,7 +727,13 @@ int fat_add_entries(struct inode *dir,in
 	offset = curr = 0;
 	*bh = NULL;
 	row = 0;
-	while (fat_get_entry(dir,&curr,bh,de,ino) > -1) {
+	while (fat_get_entry(dir, &curr, bh, de, ino) > -1) {
+		/* check the maximum size of directory */
+		if (curr >= FAT_MAX_DIR_SIZE) {
+			fat_brelse(sb, *bh);
+			return -ENOSPC;
+		}
+
 		if (IS_FREE((*de)->name)) {
 			if (++row == slots)
 				return offset;
@@ -742,7 +748,10 @@ int fat_add_entries(struct inode *dir,in
 	if (!new_bh)
 		return -ENOSPC;
 	fat_brelse(sb, new_bh);
-	do fat_get_entry(dir,&curr,bh,de,ino); while (++row<slots);
+	do {
+		fat_get_entry(dir, &curr, bh, de, ino);
+	} while (++row < slots);
+
 	return offset;
 }
 
fs/fat/inode.c +29 -21

78 lines not on this page · raw message

fs/fat/misc.c +25 -7
@@ -38,15 +38,24 @@ static char ascii_extensions[] =
  * read-only. The file system can be made writable again by remounting it.
  */
 
-void fat_fs_panic(struct super_block *s,const char *msg)
+static char panic_msg[512];
+
+void fat_fs_panic(struct super_block *s,const char *fmt, ...)
 {
 	int not_ro;
+	va_list args;
+
+	va_start (args, fmt);
+	vsnprintf (panic_msg, sizeof(panic_msg), fmt, args);
+	va_end (args);
 
 	not_ro = !(s->s_flags & MS_RDONLY);
-	if (not_ro) s->s_flags |= MS_RDONLY;
-	printk("Filesystem panic (dev %s).\n  %s\n", kdevname(s->s_dev), msg);
 	if (not_ro)
-		printk("  File system has been set read-only\n");
+		s->s_flags |= MS_RDONLY;
+	printk("FAT: Filesystem panic (dev %s)\n"
+	       "    %s\n", kdevname(s->s_dev), panic_msg);
+	if (not_ro)
+		printk("    File system has been set read-only\n");
 }
 
 
@@ -472,11 +481,13 @@ static int raw_scan_nonroot(struct super
     int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry
     **res_de)
 {
-	int count,cluster;
+	int count, cluster;
+	unsigned long dir_size;
 
 #ifdef DEBUG
 	printk("raw_scan_nonroot: start=%d\n",start);
 #endif
+	dir_size = 0;
 	do {
 		for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
 			if ((cluster = raw_scan_sector(sb,(start-2)*
@@ -484,6 +495,13 @@ static int raw_scan_nonroot(struct super
 			    count,name,number,ino,res_bh,res_de)) >= 0)
 				return cluster;
 		}
+		dir_size += 1 << MSDOS_SB(sb)->cluster_bits;
+		if (dir_size > FAT_MAX_DIR_SIZE) {
+			fat_fs_panic(sb, "Directory %d: "
+				     "exceeded the maximum size of directory",
+				     start);
+			break;
+		}
 		if (!(start = fat_access(sb,start,-1))) {
 			fat_fs_panic(sb,"FAT error");
 			break;
@@ -491,8 +509,8 @@ static int raw_scan_nonroot(struct super
 #ifdef DEBUG
 	printk("next start: %d\n",start);
 #endif
-	}
-	while (start != -1);
+	} while (start != -1);
+
 	return -ENOENT;
 }
 
include/linux/msdos_fs.h +5 -1
@@ -19,6 +19,10 @@
 #define MSDOS_DPS_BITS	4 /* log2(MSDOS_DPS) */
 #define MSDOS_DIR_BITS	5 /* log2(sizeof(struct msdos_dir_entry)) */
 
+/* directory limit */
+#define FAT_MAX_DIR_ENTRIES	(65536)
+#define FAT_MAX_DIR_SIZE	(FAT_MAX_DIR_ENTRIES << MSDOS_DIR_BITS)
+
 #define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
 
 #define FAT_CACHE    8 /* FAT cache size */
@@ -297,7 +301,7 @@ extern void fat_write_inode(struct inode
 extern int fat_notify_change(struct dentry * dentry, struct iattr * attr);
 
 /* fat/misc.c */
-extern void fat_fs_panic(struct super_block *s, const char *msg);
+extern void fat_fs_panic(struct super_block *s, const char *fmt, ...);
 extern int fat_is_binary(char conversion, char *extension);
 extern void lock_fat(struct super_block *sb);
 extern void unlock_fat(struct super_block *sb);
maintainer
#7 lore raw
In reply to Kyle
Quoted 13 lines · Andrew Morton
Quoted 12 lines · OGAWA Hirofumi
Quoted 8 lines · Andrew Morton
Quoted 6 lines

Additional check on the number of followed links could be useful there. No chain should be longer than the number of clusters on the fs. Although on large FAT32 filesystems the number of clusters can be high, a very long loop is still better than an infinite one. (In cases where we know the file size, this limit can be reduced to file_size/cluster_size + 1 links).

hmm.. OK, I'll take a look at that approach.

Quoted 8 lines
Quoted 5 lines

The maximum number of directory entries is 65536 in ordinarily. The following patch prevents the infinite loop of a directory (include the limit to added directory).

I think the following patch is a bit useful.

Ah lovely. I was looking for your email address when this came up, but couldn't find it.

Sorry, that patch is first post.

Quoted 11 lines

Here's the 2.4.18-pre3 version of your patch. I've tested it against the corrupted filesystem image and it works fine:

Jan 14 02:40:50 mnm kernel: FAT: Filesystem panic (dev 07:00)
Jan 14 02:40:50 mnm kernel:     Directory 3329: exceeded the maximum size of directory
Jan 14 02:40:50 mnm kernel:     File system has been set read-only
Jan 14 02:40:50 mnm kernel: FAT: Filesystem panic (dev 07:00)
Jan 14 02:40:50 mnm kernel:     Directory 5: exceeded the maximum size of directory

If you're satisfied that everything is OK, I'd suggest that you send the diff to Marcelo.

Thanks. That patch doesn't completely prevent the infinite loop (regular file), but I think the following patch is useful (directory).

Marcelo, please apply the following patch.

fs/fat/dir.c +11 -2
@@ -727,7 +727,13 @@ int fat_add_entries(struct inode *dir,in
 	offset = curr = 0;
 	*bh = NULL;
 	row = 0;
-	while (fat_get_entry(dir,&curr,bh,de,ino) > -1) {
+	while (fat_get_entry(dir, &curr, bh, de, ino) > -1) {
+		/* check the maximum size of directory */
+		if (curr >= FAT_MAX_DIR_SIZE) {
+			fat_brelse(sb, *bh);
+			return -ENOSPC;
+		}
+
 		if (IS_FREE((*de)->name)) {
 			if (++row == slots)
 				return offset;
@@ -742,7 +748,10 @@ int fat_add_entries(struct inode *dir,in
 	if (!new_bh)
 		return -ENOSPC;
 	fat_brelse(sb, new_bh);
-	do fat_get_entry(dir,&curr,bh,de,ino); while (++row<slots);
+	do {
+		fat_get_entry(dir, &curr, bh, de, ino);
+	} while (++row < slots);
+
 	return offset;
 }
 
fs/fat/inode.c +29 -21

78 lines not on this page · raw message

fs/fat/misc.c +25 -7
@@ -38,15 +38,24 @@ static char ascii_extensions[] =
  * read-only. The file system can be made writable again by remounting it.
  */
 
-void fat_fs_panic(struct super_block *s,const char *msg)
+static char panic_msg[512];
+
+void fat_fs_panic(struct super_block *s,const char *fmt, ...)
 {
 	int not_ro;
+	va_list args;
+
+	va_start (args, fmt);
+	vsnprintf (panic_msg, sizeof(panic_msg), fmt, args);
+	va_end (args);
 
 	not_ro = !(s->s_flags & MS_RDONLY);
-	if (not_ro) s->s_flags |= MS_RDONLY;
-	printk("Filesystem panic (dev %s).\n  %s\n", kdevname(s->s_dev), msg);
 	if (not_ro)
-		printk("  File system has been set read-only\n");
+		s->s_flags |= MS_RDONLY;
+	printk("FAT: Filesystem panic (dev %s)\n"
+	       "    %s\n", kdevname(s->s_dev), panic_msg);
+	if (not_ro)
+		printk("    File system has been set read-only\n");
 }
 
 
@@ -472,11 +481,13 @@ static int raw_scan_nonroot(struct super
     int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry
     **res_de)
 {
-	int count,cluster;
+	int count, cluster;
+	unsigned long dir_size;
 
 #ifdef DEBUG
 	printk("raw_scan_nonroot: start=%d\n",start);
 #endif
+	dir_size = 0;
 	do {
 		for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
 			if ((cluster = raw_scan_sector(sb,(start-2)*
@@ -484,6 +495,13 @@ static int raw_scan_nonroot(struct super
 			    count,name,number,ino,res_bh,res_de)) >= 0)
 				return cluster;
 		}
+		dir_size += 1 << MSDOS_SB(sb)->cluster_bits;
+		if (dir_size > FAT_MAX_DIR_SIZE) {
+			fat_fs_panic(sb, "Directory %d: "
+				     "exceeded the maximum size of directory",
+				     start);
+			break;
+		}
 		if (!(start = fat_access(sb,start,-1))) {
 			fat_fs_panic(sb,"FAT error");
 			break;
@@ -491,8 +509,8 @@ static int raw_scan_nonroot(struct super
 #ifdef DEBUG
 	printk("next start: %d\n",start);
 #endif
-	}
-	while (start != -1);
+	} while (start != -1);
+
 	return -ENOENT;
 }
 
include/linux/msdos_fs.h +5 -1
@@ -19,6 +19,10 @@
 #define MSDOS_DPS_BITS	4 /* log2(MSDOS_DPS) */
 #define MSDOS_DIR_BITS	5 /* log2(sizeof(struct msdos_dir_entry)) */
 
+/* directory limit */
+#define FAT_MAX_DIR_ENTRIES	(65536)
+#define FAT_MAX_DIR_SIZE	(FAT_MAX_DIR_ENTRIES << MSDOS_DIR_BITS)
+
 #define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
 
 #define FAT_CACHE    8 /* FAT cache size */
@@ -297,7 +301,7 @@ extern void fat_write_inode(struct inode
 extern int fat_notify_change(struct dentry * dentry, struct iattr * attr);
 
 /* fat/misc.c */
-extern void fat_fs_panic(struct super_block *s, const char *msg);
+extern void fat_fs_panic(struct super_block *s, const char *fmt, ...);
 extern int fat_is_binary(char conversion, char *extension);
 extern void lock_fat(struct super_block *sb);
 extern void unlock_fat(struct super_block *sb);
Signature · 1 line
OGAWA Hirofumi <[email protected]>