162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * fs/bfs/file.c 462306a36Sopenharmony_ci * BFS file operations. 562306a36Sopenharmony_ci * Copyright (C) 1999-2018 Tigran Aivazian <aivazian.tigran@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Make the file block allocation algorithm understand the size 862306a36Sopenharmony_ci * of the underlying block device. 962306a36Sopenharmony_ci * Copyright (C) 2007 Dmitri Vorobiev <dmitri.vorobiev@gmail.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/buffer_head.h> 1562306a36Sopenharmony_ci#include "bfs.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#undef DEBUG 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#ifdef DEBUG 2062306a36Sopenharmony_ci#define dprintf(x...) printf(x) 2162306a36Sopenharmony_ci#else 2262306a36Sopenharmony_ci#define dprintf(x...) 2362306a36Sopenharmony_ci#endif 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciconst struct file_operations bfs_file_operations = { 2662306a36Sopenharmony_ci .llseek = generic_file_llseek, 2762306a36Sopenharmony_ci .read_iter = generic_file_read_iter, 2862306a36Sopenharmony_ci .write_iter = generic_file_write_iter, 2962306a36Sopenharmony_ci .mmap = generic_file_mmap, 3062306a36Sopenharmony_ci .splice_read = filemap_splice_read, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int bfs_move_block(unsigned long from, unsigned long to, 3462306a36Sopenharmony_ci struct super_block *sb) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct buffer_head *bh, *new; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci bh = sb_bread(sb, from); 3962306a36Sopenharmony_ci if (!bh) 4062306a36Sopenharmony_ci return -EIO; 4162306a36Sopenharmony_ci new = sb_getblk(sb, to); 4262306a36Sopenharmony_ci memcpy(new->b_data, bh->b_data, bh->b_size); 4362306a36Sopenharmony_ci mark_buffer_dirty(new); 4462306a36Sopenharmony_ci bforget(bh); 4562306a36Sopenharmony_ci brelse(new); 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int bfs_move_blocks(struct super_block *sb, unsigned long start, 5062306a36Sopenharmony_ci unsigned long end, unsigned long where) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci unsigned long i; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci dprintf("%08lx-%08lx->%08lx\n", start, end, where); 5562306a36Sopenharmony_ci for (i = start; i <= end; i++) 5662306a36Sopenharmony_ci if(bfs_move_block(i, where + i, sb)) { 5762306a36Sopenharmony_ci dprintf("failed to move block %08lx -> %08lx\n", i, 5862306a36Sopenharmony_ci where + i); 5962306a36Sopenharmony_ci return -EIO; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int bfs_get_block(struct inode *inode, sector_t block, 6562306a36Sopenharmony_ci struct buffer_head *bh_result, int create) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci unsigned long phys; 6862306a36Sopenharmony_ci int err; 6962306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 7062306a36Sopenharmony_ci struct bfs_sb_info *info = BFS_SB(sb); 7162306a36Sopenharmony_ci struct bfs_inode_info *bi = BFS_I(inode); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci phys = bi->i_sblock + block; 7462306a36Sopenharmony_ci if (!create) { 7562306a36Sopenharmony_ci if (phys <= bi->i_eblock) { 7662306a36Sopenharmony_ci dprintf("c=%d, b=%08lx, phys=%09lx (granted)\n", 7762306a36Sopenharmony_ci create, (unsigned long)block, phys); 7862306a36Sopenharmony_ci map_bh(bh_result, sb, phys); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * If the file is not empty and the requested block is within the 8562306a36Sopenharmony_ci * range of blocks allocated for this file, we can grant it. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci if (bi->i_sblock && (phys <= bi->i_eblock)) { 8862306a36Sopenharmony_ci dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", 8962306a36Sopenharmony_ci create, (unsigned long)block, phys); 9062306a36Sopenharmony_ci map_bh(bh_result, sb, phys); 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* The file will be extended, so let's see if there is enough space. */ 9562306a36Sopenharmony_ci if (phys >= info->si_blocks) 9662306a36Sopenharmony_ci return -ENOSPC; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* The rest has to be protected against itself. */ 9962306a36Sopenharmony_ci mutex_lock(&info->bfs_lock); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * If the last data block for this file is the last allocated 10362306a36Sopenharmony_ci * block, we can extend the file trivially, without moving it 10462306a36Sopenharmony_ci * anywhere. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci if (bi->i_eblock == info->si_lf_eblk) { 10762306a36Sopenharmony_ci dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", 10862306a36Sopenharmony_ci create, (unsigned long)block, phys); 10962306a36Sopenharmony_ci map_bh(bh_result, sb, phys); 11062306a36Sopenharmony_ci info->si_freeb -= phys - bi->i_eblock; 11162306a36Sopenharmony_ci info->si_lf_eblk = bi->i_eblock = phys; 11262306a36Sopenharmony_ci mark_inode_dirty(inode); 11362306a36Sopenharmony_ci err = 0; 11462306a36Sopenharmony_ci goto out; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Ok, we have to move this entire file to the next free block. */ 11862306a36Sopenharmony_ci phys = info->si_lf_eblk + 1; 11962306a36Sopenharmony_ci if (phys + block >= info->si_blocks) { 12062306a36Sopenharmony_ci err = -ENOSPC; 12162306a36Sopenharmony_ci goto out; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (bi->i_sblock) { 12562306a36Sopenharmony_ci err = bfs_move_blocks(inode->i_sb, bi->i_sblock, 12662306a36Sopenharmony_ci bi->i_eblock, phys); 12762306a36Sopenharmony_ci if (err) { 12862306a36Sopenharmony_ci dprintf("failed to move ino=%08lx -> fs corruption\n", 12962306a36Sopenharmony_ci inode->i_ino); 13062306a36Sopenharmony_ci goto out; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci } else 13362306a36Sopenharmony_ci err = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci dprintf("c=%d, b=%08lx, phys=%08lx (moved)\n", 13662306a36Sopenharmony_ci create, (unsigned long)block, phys); 13762306a36Sopenharmony_ci bi->i_sblock = phys; 13862306a36Sopenharmony_ci phys += block; 13962306a36Sopenharmony_ci info->si_lf_eblk = bi->i_eblock = phys; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * This assumes nothing can write the inode back while we are here 14362306a36Sopenharmony_ci * and thus update inode->i_blocks! (XXX) 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci info->si_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks; 14662306a36Sopenharmony_ci mark_inode_dirty(inode); 14762306a36Sopenharmony_ci map_bh(bh_result, sb, phys); 14862306a36Sopenharmony_ciout: 14962306a36Sopenharmony_ci mutex_unlock(&info->bfs_lock); 15062306a36Sopenharmony_ci return err; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int bfs_writepage(struct page *page, struct writeback_control *wbc) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci return block_write_full_page(page, bfs_get_block, wbc); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int bfs_read_folio(struct file *file, struct folio *folio) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci return block_read_full_folio(folio, bfs_get_block); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void bfs_write_failed(struct address_space *mapping, loff_t to) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct inode *inode = mapping->host; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (to > inode->i_size) 16862306a36Sopenharmony_ci truncate_pagecache(inode, inode->i_size); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int bfs_write_begin(struct file *file, struct address_space *mapping, 17262306a36Sopenharmony_ci loff_t pos, unsigned len, 17362306a36Sopenharmony_ci struct page **pagep, void **fsdata) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int ret; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = block_write_begin(mapping, pos, len, pagep, bfs_get_block); 17862306a36Sopenharmony_ci if (unlikely(ret)) 17962306a36Sopenharmony_ci bfs_write_failed(mapping, pos + len); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return ret; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic sector_t bfs_bmap(struct address_space *mapping, sector_t block) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci return generic_block_bmap(mapping, block, bfs_get_block); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciconst struct address_space_operations bfs_aops = { 19062306a36Sopenharmony_ci .dirty_folio = block_dirty_folio, 19162306a36Sopenharmony_ci .invalidate_folio = block_invalidate_folio, 19262306a36Sopenharmony_ci .read_folio = bfs_read_folio, 19362306a36Sopenharmony_ci .writepage = bfs_writepage, 19462306a36Sopenharmony_ci .write_begin = bfs_write_begin, 19562306a36Sopenharmony_ci .write_end = generic_write_end, 19662306a36Sopenharmony_ci .bmap = bfs_bmap, 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciconst struct inode_operations bfs_file_inops; 200