18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fs/bfs/file.c 48c2ecf20Sopenharmony_ci * BFS file operations. 58c2ecf20Sopenharmony_ci * Copyright (C) 1999-2018 Tigran Aivazian <aivazian.tigran@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Make the file block allocation algorithm understand the size 88c2ecf20Sopenharmony_ci * of the underlying block device. 98c2ecf20Sopenharmony_ci * Copyright (C) 2007 Dmitri Vorobiev <dmitri.vorobiev@gmail.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/fs.h> 148c2ecf20Sopenharmony_ci#include <linux/buffer_head.h> 158c2ecf20Sopenharmony_ci#include "bfs.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#undef DEBUG 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#ifdef DEBUG 208c2ecf20Sopenharmony_ci#define dprintf(x...) printf(x) 218c2ecf20Sopenharmony_ci#else 228c2ecf20Sopenharmony_ci#define dprintf(x...) 238c2ecf20Sopenharmony_ci#endif 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciconst struct file_operations bfs_file_operations = { 268c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 278c2ecf20Sopenharmony_ci .read_iter = generic_file_read_iter, 288c2ecf20Sopenharmony_ci .write_iter = generic_file_write_iter, 298c2ecf20Sopenharmony_ci .mmap = generic_file_mmap, 308c2ecf20Sopenharmony_ci .splice_read = generic_file_splice_read, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int bfs_move_block(unsigned long from, unsigned long to, 348c2ecf20Sopenharmony_ci struct super_block *sb) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct buffer_head *bh, *new; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci bh = sb_bread(sb, from); 398c2ecf20Sopenharmony_ci if (!bh) 408c2ecf20Sopenharmony_ci return -EIO; 418c2ecf20Sopenharmony_ci new = sb_getblk(sb, to); 428c2ecf20Sopenharmony_ci memcpy(new->b_data, bh->b_data, bh->b_size); 438c2ecf20Sopenharmony_ci mark_buffer_dirty(new); 448c2ecf20Sopenharmony_ci bforget(bh); 458c2ecf20Sopenharmony_ci brelse(new); 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int bfs_move_blocks(struct super_block *sb, unsigned long start, 508c2ecf20Sopenharmony_ci unsigned long end, unsigned long where) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci unsigned long i; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci dprintf("%08lx-%08lx->%08lx\n", start, end, where); 558c2ecf20Sopenharmony_ci for (i = start; i <= end; i++) 568c2ecf20Sopenharmony_ci if(bfs_move_block(i, where + i, sb)) { 578c2ecf20Sopenharmony_ci dprintf("failed to move block %08lx -> %08lx\n", i, 588c2ecf20Sopenharmony_ci where + i); 598c2ecf20Sopenharmony_ci return -EIO; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci return 0; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int bfs_get_block(struct inode *inode, sector_t block, 658c2ecf20Sopenharmony_ci struct buffer_head *bh_result, int create) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci unsigned long phys; 688c2ecf20Sopenharmony_ci int err; 698c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 708c2ecf20Sopenharmony_ci struct bfs_sb_info *info = BFS_SB(sb); 718c2ecf20Sopenharmony_ci struct bfs_inode_info *bi = BFS_I(inode); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci phys = bi->i_sblock + block; 748c2ecf20Sopenharmony_ci if (!create) { 758c2ecf20Sopenharmony_ci if (phys <= bi->i_eblock) { 768c2ecf20Sopenharmony_ci dprintf("c=%d, b=%08lx, phys=%09lx (granted)\n", 778c2ecf20Sopenharmony_ci create, (unsigned long)block, phys); 788c2ecf20Sopenharmony_ci map_bh(bh_result, sb, phys); 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * If the file is not empty and the requested block is within the 858c2ecf20Sopenharmony_ci * range of blocks allocated for this file, we can grant it. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci if (bi->i_sblock && (phys <= bi->i_eblock)) { 888c2ecf20Sopenharmony_ci dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", 898c2ecf20Sopenharmony_ci create, (unsigned long)block, phys); 908c2ecf20Sopenharmony_ci map_bh(bh_result, sb, phys); 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* The file will be extended, so let's see if there is enough space. */ 958c2ecf20Sopenharmony_ci if (phys >= info->si_blocks) 968c2ecf20Sopenharmony_ci return -ENOSPC; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* The rest has to be protected against itself. */ 998c2ecf20Sopenharmony_ci mutex_lock(&info->bfs_lock); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * If the last data block for this file is the last allocated 1038c2ecf20Sopenharmony_ci * block, we can extend the file trivially, without moving it 1048c2ecf20Sopenharmony_ci * anywhere. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci if (bi->i_eblock == info->si_lf_eblk) { 1078c2ecf20Sopenharmony_ci dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", 1088c2ecf20Sopenharmony_ci create, (unsigned long)block, phys); 1098c2ecf20Sopenharmony_ci map_bh(bh_result, sb, phys); 1108c2ecf20Sopenharmony_ci info->si_freeb -= phys - bi->i_eblock; 1118c2ecf20Sopenharmony_ci info->si_lf_eblk = bi->i_eblock = phys; 1128c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 1138c2ecf20Sopenharmony_ci err = 0; 1148c2ecf20Sopenharmony_ci goto out; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Ok, we have to move this entire file to the next free block. */ 1188c2ecf20Sopenharmony_ci phys = info->si_lf_eblk + 1; 1198c2ecf20Sopenharmony_ci if (phys + block >= info->si_blocks) { 1208c2ecf20Sopenharmony_ci err = -ENOSPC; 1218c2ecf20Sopenharmony_ci goto out; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (bi->i_sblock) { 1258c2ecf20Sopenharmony_ci err = bfs_move_blocks(inode->i_sb, bi->i_sblock, 1268c2ecf20Sopenharmony_ci bi->i_eblock, phys); 1278c2ecf20Sopenharmony_ci if (err) { 1288c2ecf20Sopenharmony_ci dprintf("failed to move ino=%08lx -> fs corruption\n", 1298c2ecf20Sopenharmony_ci inode->i_ino); 1308c2ecf20Sopenharmony_ci goto out; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci } else 1338c2ecf20Sopenharmony_ci err = 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci dprintf("c=%d, b=%08lx, phys=%08lx (moved)\n", 1368c2ecf20Sopenharmony_ci create, (unsigned long)block, phys); 1378c2ecf20Sopenharmony_ci bi->i_sblock = phys; 1388c2ecf20Sopenharmony_ci phys += block; 1398c2ecf20Sopenharmony_ci info->si_lf_eblk = bi->i_eblock = phys; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* 1428c2ecf20Sopenharmony_ci * This assumes nothing can write the inode back while we are here 1438c2ecf20Sopenharmony_ci * and thus update inode->i_blocks! (XXX) 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci info->si_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks; 1468c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 1478c2ecf20Sopenharmony_ci map_bh(bh_result, sb, phys); 1488c2ecf20Sopenharmony_ciout: 1498c2ecf20Sopenharmony_ci mutex_unlock(&info->bfs_lock); 1508c2ecf20Sopenharmony_ci return err; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int bfs_writepage(struct page *page, struct writeback_control *wbc) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci return block_write_full_page(page, bfs_get_block, wbc); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int bfs_readpage(struct file *file, struct page *page) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci return block_read_full_page(page, bfs_get_block); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void bfs_write_failed(struct address_space *mapping, loff_t to) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct inode *inode = mapping->host; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (to > inode->i_size) 1688c2ecf20Sopenharmony_ci truncate_pagecache(inode, inode->i_size); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int bfs_write_begin(struct file *file, struct address_space *mapping, 1728c2ecf20Sopenharmony_ci loff_t pos, unsigned len, unsigned flags, 1738c2ecf20Sopenharmony_ci struct page **pagep, void **fsdata) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci ret = block_write_begin(mapping, pos, len, flags, pagep, 1788c2ecf20Sopenharmony_ci bfs_get_block); 1798c2ecf20Sopenharmony_ci if (unlikely(ret)) 1808c2ecf20Sopenharmony_ci bfs_write_failed(mapping, pos + len); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic sector_t bfs_bmap(struct address_space *mapping, sector_t block) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci return generic_block_bmap(mapping, block, bfs_get_block); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ciconst struct address_space_operations bfs_aops = { 1918c2ecf20Sopenharmony_ci .readpage = bfs_readpage, 1928c2ecf20Sopenharmony_ci .writepage = bfs_writepage, 1938c2ecf20Sopenharmony_ci .write_begin = bfs_write_begin, 1948c2ecf20Sopenharmony_ci .write_end = generic_write_end, 1958c2ecf20Sopenharmony_ci .bmap = bfs_bmap, 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ciconst struct inode_operations bfs_file_inops; 199