Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[linux-2.6] / fs / bfs / file.c
1 /*
2  *      fs/bfs/file.c
3  *      BFS file operations.
4  *      Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com>
5  *
6  *      Make the file block allocation algorithm understand the size
7  *      of the underlying block device.
8  *      Copyright (C) 2007 Dmitri Vorobiev <dmitri.vorobiev@gmail.com>
9  *
10  */
11
12 #include <linux/fs.h>
13 #include <linux/buffer_head.h>
14 #include <linux/smp_lock.h>
15 #include "bfs.h"
16
17 #undef DEBUG
18
19 #ifdef DEBUG
20 #define dprintf(x...)   printf(x)
21 #else
22 #define dprintf(x...)
23 #endif
24
25 const struct file_operations bfs_file_operations = {
26         .llseek         = generic_file_llseek,
27         .read           = do_sync_read,
28         .aio_read       = generic_file_aio_read,
29         .write          = do_sync_write,
30         .aio_write      = generic_file_aio_write,
31         .mmap           = generic_file_mmap,
32         .splice_read    = generic_file_splice_read,
33 };
34
35 static int bfs_move_block(unsigned long from, unsigned long to,
36                                         struct super_block *sb)
37 {
38         struct buffer_head *bh, *new;
39
40         bh = sb_bread(sb, from);
41         if (!bh)
42                 return -EIO;
43         new = sb_getblk(sb, to);
44         memcpy(new->b_data, bh->b_data, bh->b_size);
45         mark_buffer_dirty(new);
46         bforget(bh);
47         brelse(new);
48         return 0;
49 }
50
51 static int bfs_move_blocks(struct super_block *sb, unsigned long start,
52                                 unsigned long end, unsigned long where)
53 {
54         unsigned long i;
55
56         dprintf("%08lx-%08lx->%08lx\n", start, end, where);
57         for (i = start; i <= end; i++)
58                 if(bfs_move_block(i, where + i, sb)) {
59                         dprintf("failed to move block %08lx -> %08lx\n", i,
60                                                                 where + i);
61                         return -EIO;
62                 }
63         return 0;
64 }
65
66 static int bfs_get_block(struct inode *inode, sector_t block,
67                         struct buffer_head *bh_result, int create)
68 {
69         unsigned long phys;
70         int err;
71         struct super_block *sb = inode->i_sb;
72         struct bfs_sb_info *info = BFS_SB(sb);
73         struct bfs_inode_info *bi = BFS_I(inode);
74         struct buffer_head *sbh = info->si_sbh;
75
76         phys = bi->i_sblock + block;
77         if (!create) {
78                 if (phys <= bi->i_eblock) {
79                         dprintf("c=%d, b=%08lx, phys=%09lx (granted)\n",
80                                 create, (unsigned long)block, phys);
81                         map_bh(bh_result, sb, phys);
82                 }
83                 return 0;
84         }
85
86         /*
87          * If the file is not empty and the requested block is within the
88          * range of blocks allocated for this file, we can grant it.
89          */
90         if (bi->i_sblock && (phys <= bi->i_eblock)) {
91                 dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", 
92                                 create, (unsigned long)block, phys);
93                 map_bh(bh_result, sb, phys);
94                 return 0;
95         }
96
97         /* The file will be extended, so let's see if there is enough space. */
98         if (phys >= info->si_blocks)
99                 return -ENOSPC;
100
101         /* The rest has to be protected against itself. */
102         mutex_lock(&info->bfs_lock);
103
104         /*
105          * If the last data block for this file is the last allocated
106          * block, we can extend the file trivially, without moving it
107          * anywhere.
108          */
109         if (bi->i_eblock == info->si_lf_eblk) {
110                 dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", 
111                                 create, (unsigned long)block, phys);
112                 map_bh(bh_result, sb, phys);
113                 info->si_freeb -= phys - bi->i_eblock;
114                 info->si_lf_eblk = bi->i_eblock = phys;
115                 mark_inode_dirty(inode);
116                 mark_buffer_dirty(sbh);
117                 err = 0;
118                 goto out;
119         }
120
121         /* Ok, we have to move this entire file to the next free block. */
122         phys = info->si_lf_eblk + 1;
123         if (phys + block >= info->si_blocks) {
124                 err = -ENOSPC;
125                 goto out;
126         }
127
128         if (bi->i_sblock) {
129                 err = bfs_move_blocks(inode->i_sb, bi->i_sblock, 
130                                                 bi->i_eblock, phys);
131                 if (err) {
132                         dprintf("failed to move ino=%08lx -> fs corruption\n",
133                                                                 inode->i_ino);
134                         goto out;
135                 }
136         } else
137                 err = 0;
138
139         dprintf("c=%d, b=%08lx, phys=%08lx (moved)\n",
140                 create, (unsigned long)block, phys);
141         bi->i_sblock = phys;
142         phys += block;
143         info->si_lf_eblk = bi->i_eblock = phys;
144
145         /*
146          * This assumes nothing can write the inode back while we are here
147          * and thus update inode->i_blocks! (XXX)
148          */
149         info->si_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks;
150         mark_inode_dirty(inode);
151         mark_buffer_dirty(sbh);
152         map_bh(bh_result, sb, phys);
153 out:
154         mutex_unlock(&info->bfs_lock);
155         return err;
156 }
157
158 static int bfs_writepage(struct page *page, struct writeback_control *wbc)
159 {
160         return block_write_full_page(page, bfs_get_block, wbc);
161 }
162
163 static int bfs_readpage(struct file *file, struct page *page)
164 {
165         return block_read_full_page(page, bfs_get_block);
166 }
167
168 static int bfs_write_begin(struct file *file, struct address_space *mapping,
169                         loff_t pos, unsigned len, unsigned flags,
170                         struct page **pagep, void **fsdata)
171 {
172         *pagep = NULL;
173         return block_write_begin(file, mapping, pos, len, flags,
174                                         pagep, fsdata, bfs_get_block);
175 }
176
177 static sector_t bfs_bmap(struct address_space *mapping, sector_t block)
178 {
179         return generic_block_bmap(mapping, block, bfs_get_block);
180 }
181
182 const struct address_space_operations bfs_aops = {
183         .readpage       = bfs_readpage,
184         .writepage      = bfs_writepage,
185         .sync_page      = block_sync_page,
186         .write_begin    = bfs_write_begin,
187         .write_end      = generic_write_end,
188         .bmap           = bfs_bmap,
189 };
190
191 const struct inode_operations bfs_file_inops;