summaryrefslogtreecommitdiffstats
path: root/fs/bfs/file.c
diff options
context:
space:
mode:
authorDmitri Vorobiev <dmitri.vorobiev@gmail.com>2007-11-14 16:59:47 -0800
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-11-14 18:45:40 -0800
commitf433dc56344cb72cc3de5ba0819021cec3aef807 (patch)
tree19fa9052a2787c84417dc306eda1eae8b02f14f2 /fs/bfs/file.c
parentcfb5285660aad4931b2ebbfa902ea48a37dfffa1 (diff)
downloadlinux-f433dc56344cb72cc3de5ba0819021cec3aef807.tar.gz
linux-f433dc56344cb72cc3de5ba0819021cec3aef807.tar.bz2
linux-f433dc56344cb72cc3de5ba0819021cec3aef807.zip
Fixes to the BFS filesystem driver
I found a few bugs in the BFS driver. Detailed description of the bugs as well as the steps to reproduce the errors are given in the kernel bugzilla. Please follow these links for more information: http://bugzilla.kernel.org/show_bug.cgi?id=9363 http://bugzilla.kernel.org/show_bug.cgi?id=9364 http://bugzilla.kernel.org/show_bug.cgi?id=9365 http://bugzilla.kernel.org/show_bug.cgi?id=9366 This patch fixes the bugs described above. Besides, the patch introduces coding style changes to make the BFS driver conform to the requirements specified for Linux kernel code. Finally, I made a few cosmetic changes such as removal of trivial debug output. Also, the patch removes the fields `si_lf_ioff' and `si_lf_sblk' of the in-core superblock structure. These fields are initialized but never actually used. If you are wondering why I need BFS, here is the answer: I am using this driver in the context of Linux kernel classes I am teaching in the Moscow State University and in the International Institute of Information Technology in Pune, India. Signed-off-by: Dmitri Vorobiev <dmitri.vorobiev@gmail.com> Cc: Tigran Aivazian <tigran@veritas.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/bfs/file.c')
-rw-r--r--fs/bfs/file.c62
1 files changed, 41 insertions, 21 deletions
diff --git a/fs/bfs/file.c b/fs/bfs/file.c
index 911b4ccf470f..b11e63e8fbcd 100644
--- a/fs/bfs/file.c
+++ b/fs/bfs/file.c
@@ -2,6 +2,11 @@
* fs/bfs/file.c
* BFS file operations.
* Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com>
+ *
+ * Make the file block allocation algorithm understand the size
+ * of the underlying block device.
+ * Copyright (C) 2007 Dmitri Vorobiev <dmitri.vorobiev@gmail.com>
+ *
*/
#include <linux/fs.h>
@@ -27,7 +32,8 @@ const struct file_operations bfs_file_operations = {
.splice_read = generic_file_splice_read,
};
-static int bfs_move_block(unsigned long from, unsigned long to, struct super_block *sb)
+static int bfs_move_block(unsigned long from, unsigned long to,
+ struct super_block *sb)
{
struct buffer_head *bh, *new;
@@ -43,21 +49,22 @@ static int bfs_move_block(unsigned long from, unsigned long to, struct super_blo
}
static int bfs_move_blocks(struct super_block *sb, unsigned long start,
- unsigned long end, unsigned long where)
+ unsigned long end, unsigned long where)
{
unsigned long i;
dprintf("%08lx-%08lx->%08lx\n", start, end, where);
for (i = start; i <= end; i++)
if(bfs_move_block(i, where + i, sb)) {
- dprintf("failed to move block %08lx -> %08lx\n", i, where + i);
+ dprintf("failed to move block %08lx -> %08lx\n", i,
+ where + i);
return -EIO;
}
return 0;
}
-static int bfs_get_block(struct inode * inode, sector_t block,
- struct buffer_head * bh_result, int create)
+static int bfs_get_block(struct inode *inode, sector_t block,
+ struct buffer_head *bh_result, int create)
{
unsigned long phys;
int err;
@@ -66,9 +73,6 @@ static int bfs_get_block(struct inode * inode, sector_t block,
struct bfs_inode_info *bi = BFS_I(inode);
struct buffer_head *sbh = info->si_sbh;
- if (block > info->si_blocks)
- return -EIO;
-
phys = bi->i_sblock + block;
if (!create) {
if (phys <= bi->i_eblock) {
@@ -79,21 +83,29 @@ static int bfs_get_block(struct inode * inode, sector_t block,
return 0;
}
- /* if the file is not empty and the requested block is within the range
- of blocks allocated for this file, we can grant it */
- if (inode->i_size && phys <= bi->i_eblock) {
+ /*
+ * If the file is not empty and the requested block is within the
+ * range of blocks allocated for this file, we can grant it.
+ */
+ if (bi->i_sblock && (phys <= bi->i_eblock)) {
dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n",
create, (unsigned long)block, phys);
map_bh(bh_result, sb, phys);
return 0;
}
- /* the rest has to be protected against itself */
+ /* The file will be extended, so let's see if there is enough space. */
+ if (phys >= info->si_blocks)
+ return -ENOSPC;
+
+ /* The rest has to be protected against itself. */
lock_kernel();
- /* if the last data block for this file is the last allocated
- block, we can extend the file trivially, without moving it
- anywhere */
+ /*
+ * If the last data block for this file is the last allocated
+ * block, we can extend the file trivially, without moving it
+ * anywhere.
+ */
if (bi->i_eblock == info->si_lf_eblk) {
dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n",
create, (unsigned long)block, phys);
@@ -106,13 +118,19 @@ static int bfs_get_block(struct inode * inode, sector_t block,
goto out;
}
- /* Ok, we have to move this entire file to the next free block */
+ /* Ok, we have to move this entire file to the next free block. */
phys = info->si_lf_eblk + 1;
- if (bi->i_sblock) { /* if data starts on block 0 then there is no data */
+ if (phys + block >= info->si_blocks) {
+ err = -ENOSPC;
+ goto out;
+ }
+
+ if (bi->i_sblock) {
err = bfs_move_blocks(inode->i_sb, bi->i_sblock,
- bi->i_eblock, phys);
+ bi->i_eblock, phys);
if (err) {
- dprintf("failed to move ino=%08lx -> fs corruption\n", inode->i_ino);
+ dprintf("failed to move ino=%08lx -> fs corruption\n",
+ inode->i_ino);
goto out;
}
} else
@@ -124,8 +142,10 @@ static int bfs_get_block(struct inode * inode, sector_t block,
phys += block;
info->si_lf_eblk = bi->i_eblock = phys;
- /* this assumes nothing can write the inode back while we are here
- * and thus update inode->i_blocks! (XXX)*/
+ /*
+ * This assumes nothing can write the inode back while we are here
+ * and thus update inode->i_blocks! (XXX)
+ */
info->si_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks;
mark_inode_dirty(inode);
mark_buffer_dirty(sbh);