Merge branch 'master' into for_paulus
[linux-drm-fsl-dcu.git] / 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
7 #include <linux/fs.h>
8 #include <linux/buffer_head.h>
9 #include <linux/smp_lock.h>
10 #include "bfs.h"
11
12 #undef DEBUG
13
14 #ifdef DEBUG
15 #define dprintf(x...)   printf(x)
16 #else
17 #define dprintf(x...)
18 #endif
19
20 const struct file_operations bfs_file_operations = {
21         .llseek         = generic_file_llseek,
22         .read           = do_sync_read,
23         .aio_read       = generic_file_aio_read,
24         .write          = do_sync_write,
25         .aio_write      = generic_file_aio_write,
26         .mmap           = generic_file_mmap,
27         .sendfile       = generic_file_sendfile,
28 };
29
30 static int bfs_move_block(unsigned long from, unsigned long to, struct super_block *sb)
31 {
32         struct buffer_head *bh, *new;
33
34         bh = sb_bread(sb, from);
35         if (!bh)
36                 return -EIO;
37         new = sb_getblk(sb, to);
38         memcpy(new->b_data, bh->b_data, bh->b_size);
39         mark_buffer_dirty(new);
40         bforget(bh);
41         brelse(new);
42         return 0;
43 }
44
45 static int bfs_move_blocks(struct super_block *sb, unsigned long start,
46                            unsigned long end, unsigned long where)
47 {
48         unsigned long i;
49
50         dprintf("%08lx-%08lx->%08lx\n", start, end, where);
51         for (i = start; i <= end; i++)
52                 if(bfs_move_block(i, where + i, sb)) {
53                         dprintf("failed to move block %08lx -> %08lx\n", i, where + i);
54                         return -EIO;
55                 }
56         return 0;
57 }
58
59 static int bfs_get_block(struct inode * inode, sector_t block, 
60         struct buffer_head * bh_result, int create)
61 {
62         unsigned long phys;
63         int err;
64         struct super_block *sb = inode->i_sb;
65         struct bfs_sb_info *info = BFS_SB(sb);
66         struct bfs_inode_info *bi = BFS_I(inode);
67         struct buffer_head *sbh = info->si_sbh;
68
69         if (block > info->si_blocks)
70                 return -EIO;
71
72         phys = bi->i_sblock + block;
73         if (!create) {
74                 if (phys <= bi->i_eblock) {
75                         dprintf("c=%d, b=%08lx, phys=%09lx (granted)\n",
76                                 create, (unsigned long)block, phys);
77                         map_bh(bh_result, sb, phys);
78                 }
79                 return 0;
80         }
81
82         /* if the file is not empty and the requested block is within the range
83            of blocks allocated for this file, we can grant it */
84         if (inode->i_size && phys <= bi->i_eblock) {
85                 dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", 
86                                 create, (unsigned long)block, phys);
87                 map_bh(bh_result, sb, phys);
88                 return 0;
89         }
90
91         /* the rest has to be protected against itself */
92         lock_kernel();
93
94         /* if the last data block for this file is the last allocated
95            block, we can extend the file trivially, without moving it
96            anywhere */
97         if (bi->i_eblock == info->si_lf_eblk) {
98                 dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", 
99                                 create, (unsigned long)block, phys);
100                 map_bh(bh_result, sb, phys);
101                 info->si_freeb -= phys - bi->i_eblock;
102                 info->si_lf_eblk = bi->i_eblock = phys;
103                 mark_inode_dirty(inode);
104                 mark_buffer_dirty(sbh);
105                 err = 0;
106                 goto out;
107         }
108
109         /* Ok, we have to move this entire file to the next free block */
110         phys = info->si_lf_eblk + 1;
111         if (bi->i_sblock) { /* if data starts on block 0 then there is no data */
112                 err = bfs_move_blocks(inode->i_sb, bi->i_sblock, 
113                                 bi->i_eblock, phys);
114                 if (err) {
115                         dprintf("failed to move ino=%08lx -> fs corruption\n", inode->i_ino);
116                         goto out;
117                 }
118         } else
119                 err = 0;
120
121         dprintf("c=%d, b=%08lx, phys=%08lx (moved)\n",
122                 create, (unsigned long)block, phys);
123         bi->i_sblock = phys;
124         phys += block;
125         info->si_lf_eblk = bi->i_eblock = phys;
126
127         /* this assumes nothing can write the inode back while we are here
128          * and thus update inode->i_blocks! (XXX)*/
129         info->si_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks;
130         mark_inode_dirty(inode);
131         mark_buffer_dirty(sbh);
132         map_bh(bh_result, sb, phys);
133 out:
134         unlock_kernel();
135         return err;
136 }
137
138 static int bfs_writepage(struct page *page, struct writeback_control *wbc)
139 {
140         return block_write_full_page(page, bfs_get_block, wbc);
141 }
142
143 static int bfs_readpage(struct file *file, struct page *page)
144 {
145         return block_read_full_page(page, bfs_get_block);
146 }
147
148 static int bfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
149 {
150         return block_prepare_write(page, from, to, bfs_get_block);
151 }
152
153 static sector_t bfs_bmap(struct address_space *mapping, sector_t block)
154 {
155         return generic_block_bmap(mapping, block, bfs_get_block);
156 }
157
158 const struct address_space_operations bfs_aops = {
159         .readpage       = bfs_readpage,
160         .writepage      = bfs_writepage,
161         .sync_page      = block_sync_page,
162         .prepare_write  = bfs_prepare_write,
163         .commit_write   = generic_commit_write,
164         .bmap           = bfs_bmap,
165 };
166
167 const struct inode_operations bfs_file_inops;