| /* | |
| * fs/bfs/file.c | |
| * BFS file operations. | |
| * Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com> | |
| */ | |
| #include <linux/fs.h> | |
| #include <linux/buffer_head.h> | |
| #include <linux/smp_lock.h> | |
| #include "bfs.h" | |
| #undef DEBUG | |
| #ifdef DEBUG | |
| #define dprintf(x...) printf(x) | |
| #else | |
| #define dprintf(x...) | |
| #endif | |
| struct file_operations bfs_file_operations = { | |
| .llseek = generic_file_llseek, | |
| .read = generic_file_read, | |
| .write = generic_file_write, | |
| .mmap = generic_file_mmap, | |
| .sendfile = generic_file_sendfile, | |
| }; | |
| static int bfs_move_block(unsigned long from, unsigned long to, struct super_block *sb) | |
| { | |
| struct buffer_head *bh, *new; | |
| bh = sb_bread(sb, from); | |
| if (!bh) | |
| return -EIO; | |
| new = sb_getblk(sb, to); | |
| memcpy(new->b_data, bh->b_data, bh->b_size); | |
| mark_buffer_dirty(new); | |
| bforget(bh); | |
| brelse(new); | |
| return 0; | |
| } | |
| static int bfs_move_blocks(struct super_block *sb, unsigned long start, 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); | |
| return -EIO; | |
| } | |
| return 0; | |
| } | |
| static int bfs_get_block(struct inode * inode, sector_t block, | |
| struct buffer_head * bh_result, int create) | |
| { | |
| long phys; | |
| int err; | |
| struct super_block *sb = inode->i_sb; | |
| struct bfs_sb_info *info = BFS_SB(sb); | |
| struct bfs_inode_info *bi = BFS_I(inode); | |
| struct buffer_head *sbh = info->si_sbh; | |
| if (block < 0 || block > info->si_blocks) | |
| return -EIO; | |
| phys = bi->i_sblock + block; | |
| if (!create) { | |
| if (phys <= bi->i_eblock) { | |
| dprintf("c=%d, b=%08lx, phys=%08lx (granted)\n", create, block, phys); | |
| map_bh(bh_result, sb, phys); | |
| } | |
| 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) { | |
| dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", | |
| create, block, phys); | |
| map_bh(bh_result, sb, phys); | |
| return 0; | |
| } | |
| /* 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 (bi->i_eblock == info->si_lf_eblk) { | |
| dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", | |
| create, block, phys); | |
| map_bh(bh_result, sb, phys); | |
| info->si_freeb -= phys - bi->i_eblock; | |
| info->si_lf_eblk = bi->i_eblock = phys; | |
| mark_inode_dirty(inode); | |
| mark_buffer_dirty(sbh); | |
| err = 0; | |
| goto out; | |
| } | |
| /* 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 */ | |
| err = bfs_move_blocks(inode->i_sb, bi->i_sblock, | |
| bi->i_eblock, phys); | |
| if (err) { | |
| dprintf("failed to move ino=%08lx -> fs corruption\n", inode->i_ino); | |
| goto out; | |
| } | |
| } else | |
| err = 0; | |
| dprintf("c=%d, b=%08lx, phys=%08lx (moved)\n", create, block, phys); | |
| bi->i_sblock = phys; | |
| 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)*/ | |
| info->si_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks; | |
| mark_inode_dirty(inode); | |
| mark_buffer_dirty(sbh); | |
| map_bh(bh_result, sb, phys); | |
| out: | |
| unlock_kernel(); | |
| return err; | |
| } | |
| static int bfs_writepage(struct page *page, struct writeback_control *wbc) | |
| { | |
| return block_write_full_page(page, bfs_get_block, wbc); | |
| } | |
| static int bfs_readpage(struct file *file, struct page *page) | |
| { | |
| return block_read_full_page(page, bfs_get_block); | |
| } | |
| static int bfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) | |
| { | |
| return block_prepare_write(page, from, to, bfs_get_block); | |
| } | |
| static sector_t bfs_bmap(struct address_space *mapping, sector_t block) | |
| { | |
| return generic_block_bmap(mapping, block, bfs_get_block); | |
| } | |
| struct address_space_operations bfs_aops = { | |
| .readpage = bfs_readpage, | |
| .writepage = bfs_writepage, | |
| .sync_page = block_sync_page, | |
| .prepare_write = bfs_prepare_write, | |
| .commit_write = generic_commit_write, | |
| .bmap = bfs_bmap, | |
| }; | |
| struct inode_operations bfs_file_inops; |