162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMFS (as used by RIO Karma) file operations. 462306a36Sopenharmony_ci * Copyright (C) 2005 Bob Copeland <me@bobcopeland.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci#include <linux/buffer_head.h> 1062306a36Sopenharmony_ci#include <linux/mpage.h> 1162306a36Sopenharmony_ci#include "omfs.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic u32 omfs_max_extents(struct omfs_sb_info *sbi, int offset) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci return (sbi->s_sys_blocksize - offset - 1662306a36Sopenharmony_ci sizeof(struct omfs_extent)) / 1762306a36Sopenharmony_ci sizeof(struct omfs_extent_entry); 1862306a36Sopenharmony_ci} 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_civoid omfs_make_empty_table(struct buffer_head *bh, int offset) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct omfs_extent *oe = (struct omfs_extent *) &bh->b_data[offset]; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci oe->e_next = ~cpu_to_be64(0ULL); 2562306a36Sopenharmony_ci oe->e_extent_count = cpu_to_be32(1), 2662306a36Sopenharmony_ci oe->e_fill = cpu_to_be32(0x22), 2762306a36Sopenharmony_ci oe->e_entry[0].e_cluster = ~cpu_to_be64(0ULL); 2862306a36Sopenharmony_ci oe->e_entry[0].e_blocks = ~cpu_to_be64(0ULL); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciint omfs_shrink_inode(struct inode *inode) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb); 3462306a36Sopenharmony_ci struct omfs_extent *oe; 3562306a36Sopenharmony_ci struct omfs_extent_entry *entry; 3662306a36Sopenharmony_ci struct buffer_head *bh; 3762306a36Sopenharmony_ci u64 next, last; 3862306a36Sopenharmony_ci u32 extent_count; 3962306a36Sopenharmony_ci u32 max_extents; 4062306a36Sopenharmony_ci int ret; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* traverse extent table, freeing each entry that is greater 4362306a36Sopenharmony_ci * than inode->i_size; 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci next = inode->i_ino; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* only support truncate -> 0 for now */ 4862306a36Sopenharmony_ci ret = -EIO; 4962306a36Sopenharmony_ci if (inode->i_size != 0) 5062306a36Sopenharmony_ci goto out; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci bh = omfs_bread(inode->i_sb, next); 5362306a36Sopenharmony_ci if (!bh) 5462306a36Sopenharmony_ci goto out; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci oe = (struct omfs_extent *)(&bh->b_data[OMFS_EXTENT_START]); 5762306a36Sopenharmony_ci max_extents = omfs_max_extents(sbi, OMFS_EXTENT_START); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci for (;;) { 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (omfs_is_bad(sbi, (struct omfs_header *) bh->b_data, next)) 6262306a36Sopenharmony_ci goto out_brelse; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci extent_count = be32_to_cpu(oe->e_extent_count); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (extent_count > max_extents) 6762306a36Sopenharmony_ci goto out_brelse; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci last = next; 7062306a36Sopenharmony_ci next = be64_to_cpu(oe->e_next); 7162306a36Sopenharmony_ci entry = oe->e_entry; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* ignore last entry as it is the terminator */ 7462306a36Sopenharmony_ci for (; extent_count > 1; extent_count--) { 7562306a36Sopenharmony_ci u64 start, count; 7662306a36Sopenharmony_ci start = be64_to_cpu(entry->e_cluster); 7762306a36Sopenharmony_ci count = be64_to_cpu(entry->e_blocks); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci omfs_clear_range(inode->i_sb, start, (int) count); 8062306a36Sopenharmony_ci entry++; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci omfs_make_empty_table(bh, (char *) oe - bh->b_data); 8362306a36Sopenharmony_ci mark_buffer_dirty(bh); 8462306a36Sopenharmony_ci brelse(bh); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (last != inode->i_ino) 8762306a36Sopenharmony_ci omfs_clear_range(inode->i_sb, last, sbi->s_mirrors); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (next == ~0) 9062306a36Sopenharmony_ci break; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci bh = omfs_bread(inode->i_sb, next); 9362306a36Sopenharmony_ci if (!bh) 9462306a36Sopenharmony_ci goto out; 9562306a36Sopenharmony_ci oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]); 9662306a36Sopenharmony_ci max_extents = omfs_max_extents(sbi, OMFS_EXTENT_CONT); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci ret = 0; 9962306a36Sopenharmony_ciout: 10062306a36Sopenharmony_ci return ret; 10162306a36Sopenharmony_ciout_brelse: 10262306a36Sopenharmony_ci brelse(bh); 10362306a36Sopenharmony_ci return ret; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void omfs_truncate(struct inode *inode) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci omfs_shrink_inode(inode); 10962306a36Sopenharmony_ci mark_inode_dirty(inode); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * Add new blocks to the current extent, or create new entries/continuations 11462306a36Sopenharmony_ci * as necessary. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistatic int omfs_grow_extent(struct inode *inode, struct omfs_extent *oe, 11762306a36Sopenharmony_ci u64 *ret_block) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct omfs_extent_entry *terminator; 12062306a36Sopenharmony_ci struct omfs_extent_entry *entry = oe->e_entry; 12162306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb); 12262306a36Sopenharmony_ci u32 extent_count = be32_to_cpu(oe->e_extent_count); 12362306a36Sopenharmony_ci u64 new_block = 0; 12462306a36Sopenharmony_ci u32 max_count; 12562306a36Sopenharmony_ci int new_count; 12662306a36Sopenharmony_ci int ret = 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* reached the end of the extent table with no blocks mapped. 12962306a36Sopenharmony_ci * there are three possibilities for adding: grow last extent, 13062306a36Sopenharmony_ci * add a new extent to the current extent table, and add a 13162306a36Sopenharmony_ci * continuation inode. in last two cases need an allocator for 13262306a36Sopenharmony_ci * sbi->s_cluster_size 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* TODO: handle holes */ 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* should always have a terminator */ 13862306a36Sopenharmony_ci if (extent_count < 1) 13962306a36Sopenharmony_ci return -EIO; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* trivially grow current extent, if next block is not taken */ 14262306a36Sopenharmony_ci terminator = entry + extent_count - 1; 14362306a36Sopenharmony_ci if (extent_count > 1) { 14462306a36Sopenharmony_ci entry = terminator-1; 14562306a36Sopenharmony_ci new_block = be64_to_cpu(entry->e_cluster) + 14662306a36Sopenharmony_ci be64_to_cpu(entry->e_blocks); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (omfs_allocate_block(inode->i_sb, new_block)) { 14962306a36Sopenharmony_ci be64_add_cpu(&entry->e_blocks, 1); 15062306a36Sopenharmony_ci terminator->e_blocks = ~(cpu_to_be64( 15162306a36Sopenharmony_ci be64_to_cpu(~terminator->e_blocks) + 1)); 15262306a36Sopenharmony_ci goto out; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci max_count = omfs_max_extents(sbi, OMFS_EXTENT_START); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* TODO: add a continuation block here */ 15862306a36Sopenharmony_ci if (be32_to_cpu(oe->e_extent_count) > max_count-1) 15962306a36Sopenharmony_ci return -EIO; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* try to allocate a new cluster */ 16262306a36Sopenharmony_ci ret = omfs_allocate_range(inode->i_sb, 1, sbi->s_clustersize, 16362306a36Sopenharmony_ci &new_block, &new_count); 16462306a36Sopenharmony_ci if (ret) 16562306a36Sopenharmony_ci goto out_fail; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* copy terminator down an entry */ 16862306a36Sopenharmony_ci entry = terminator; 16962306a36Sopenharmony_ci terminator++; 17062306a36Sopenharmony_ci memcpy(terminator, entry, sizeof(struct omfs_extent_entry)); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci entry->e_cluster = cpu_to_be64(new_block); 17362306a36Sopenharmony_ci entry->e_blocks = cpu_to_be64((u64) new_count); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci terminator->e_blocks = ~(cpu_to_be64( 17662306a36Sopenharmony_ci be64_to_cpu(~terminator->e_blocks) + (u64) new_count)); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* write in new entry */ 17962306a36Sopenharmony_ci be32_add_cpu(&oe->e_extent_count, 1); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciout: 18262306a36Sopenharmony_ci *ret_block = new_block; 18362306a36Sopenharmony_ciout_fail: 18462306a36Sopenharmony_ci return ret; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* 18862306a36Sopenharmony_ci * Scans across the directory table for a given file block number. 18962306a36Sopenharmony_ci * If block not found, return 0. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistatic sector_t find_block(struct inode *inode, struct omfs_extent_entry *ent, 19262306a36Sopenharmony_ci sector_t block, int count, int *left) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci /* count > 1 because of terminator */ 19562306a36Sopenharmony_ci sector_t searched = 0; 19662306a36Sopenharmony_ci for (; count > 1; count--) { 19762306a36Sopenharmony_ci int numblocks = clus_to_blk(OMFS_SB(inode->i_sb), 19862306a36Sopenharmony_ci be64_to_cpu(ent->e_blocks)); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (block >= searched && 20162306a36Sopenharmony_ci block < searched + numblocks) { 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci * found it at cluster + (block - searched) 20462306a36Sopenharmony_ci * numblocks - (block - searched) is remainder 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci *left = numblocks - (block - searched); 20762306a36Sopenharmony_ci return clus_to_blk(OMFS_SB(inode->i_sb), 20862306a36Sopenharmony_ci be64_to_cpu(ent->e_cluster)) + 20962306a36Sopenharmony_ci block - searched; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci searched += numblocks; 21262306a36Sopenharmony_ci ent++; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int omfs_get_block(struct inode *inode, sector_t block, 21862306a36Sopenharmony_ci struct buffer_head *bh_result, int create) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct buffer_head *bh; 22162306a36Sopenharmony_ci sector_t next, offset; 22262306a36Sopenharmony_ci int ret; 22362306a36Sopenharmony_ci u64 new_block; 22462306a36Sopenharmony_ci u32 max_extents; 22562306a36Sopenharmony_ci int extent_count; 22662306a36Sopenharmony_ci struct omfs_extent *oe; 22762306a36Sopenharmony_ci struct omfs_extent_entry *entry; 22862306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb); 22962306a36Sopenharmony_ci int max_blocks = bh_result->b_size >> inode->i_blkbits; 23062306a36Sopenharmony_ci int remain; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = -EIO; 23362306a36Sopenharmony_ci bh = omfs_bread(inode->i_sb, inode->i_ino); 23462306a36Sopenharmony_ci if (!bh) 23562306a36Sopenharmony_ci goto out; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci oe = (struct omfs_extent *)(&bh->b_data[OMFS_EXTENT_START]); 23862306a36Sopenharmony_ci max_extents = omfs_max_extents(sbi, OMFS_EXTENT_START); 23962306a36Sopenharmony_ci next = inode->i_ino; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci for (;;) { 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (omfs_is_bad(sbi, (struct omfs_header *) bh->b_data, next)) 24462306a36Sopenharmony_ci goto out_brelse; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci extent_count = be32_to_cpu(oe->e_extent_count); 24762306a36Sopenharmony_ci next = be64_to_cpu(oe->e_next); 24862306a36Sopenharmony_ci entry = oe->e_entry; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (extent_count > max_extents) 25162306a36Sopenharmony_ci goto out_brelse; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci offset = find_block(inode, entry, block, extent_count, &remain); 25462306a36Sopenharmony_ci if (offset > 0) { 25562306a36Sopenharmony_ci ret = 0; 25662306a36Sopenharmony_ci map_bh(bh_result, inode->i_sb, offset); 25762306a36Sopenharmony_ci if (remain > max_blocks) 25862306a36Sopenharmony_ci remain = max_blocks; 25962306a36Sopenharmony_ci bh_result->b_size = (remain << inode->i_blkbits); 26062306a36Sopenharmony_ci goto out_brelse; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci if (next == ~0) 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci brelse(bh); 26662306a36Sopenharmony_ci bh = omfs_bread(inode->i_sb, next); 26762306a36Sopenharmony_ci if (!bh) 26862306a36Sopenharmony_ci goto out; 26962306a36Sopenharmony_ci oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]); 27062306a36Sopenharmony_ci max_extents = omfs_max_extents(sbi, OMFS_EXTENT_CONT); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci if (create) { 27362306a36Sopenharmony_ci ret = omfs_grow_extent(inode, oe, &new_block); 27462306a36Sopenharmony_ci if (ret == 0) { 27562306a36Sopenharmony_ci mark_buffer_dirty(bh); 27662306a36Sopenharmony_ci mark_inode_dirty(inode); 27762306a36Sopenharmony_ci map_bh(bh_result, inode->i_sb, 27862306a36Sopenharmony_ci clus_to_blk(sbi, new_block)); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ciout_brelse: 28262306a36Sopenharmony_ci brelse(bh); 28362306a36Sopenharmony_ciout: 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int omfs_read_folio(struct file *file, struct folio *folio) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci return block_read_full_folio(folio, omfs_get_block); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic void omfs_readahead(struct readahead_control *rac) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci mpage_readahead(rac, omfs_get_block); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic int 29862306a36Sopenharmony_ciomfs_writepages(struct address_space *mapping, struct writeback_control *wbc) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci return mpage_writepages(mapping, wbc, omfs_get_block); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic void omfs_write_failed(struct address_space *mapping, loff_t to) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct inode *inode = mapping->host; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (to > inode->i_size) { 30862306a36Sopenharmony_ci truncate_pagecache(inode, inode->i_size); 30962306a36Sopenharmony_ci omfs_truncate(inode); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int omfs_write_begin(struct file *file, struct address_space *mapping, 31462306a36Sopenharmony_ci loff_t pos, unsigned len, 31562306a36Sopenharmony_ci struct page **pagep, void **fsdata) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ret = block_write_begin(mapping, pos, len, pagep, omfs_get_block); 32062306a36Sopenharmony_ci if (unlikely(ret)) 32162306a36Sopenharmony_ci omfs_write_failed(mapping, pos + len); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return ret; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic sector_t omfs_bmap(struct address_space *mapping, sector_t block) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci return generic_block_bmap(mapping, block, omfs_get_block); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ciconst struct file_operations omfs_file_operations = { 33262306a36Sopenharmony_ci .llseek = generic_file_llseek, 33362306a36Sopenharmony_ci .read_iter = generic_file_read_iter, 33462306a36Sopenharmony_ci .write_iter = generic_file_write_iter, 33562306a36Sopenharmony_ci .mmap = generic_file_mmap, 33662306a36Sopenharmony_ci .fsync = generic_file_fsync, 33762306a36Sopenharmony_ci .splice_read = filemap_splice_read, 33862306a36Sopenharmony_ci}; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int omfs_setattr(struct mnt_idmap *idmap, 34162306a36Sopenharmony_ci struct dentry *dentry, struct iattr *attr) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 34462306a36Sopenharmony_ci int error; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci error = setattr_prepare(&nop_mnt_idmap, dentry, attr); 34762306a36Sopenharmony_ci if (error) 34862306a36Sopenharmony_ci return error; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if ((attr->ia_valid & ATTR_SIZE) && 35162306a36Sopenharmony_ci attr->ia_size != i_size_read(inode)) { 35262306a36Sopenharmony_ci error = inode_newsize_ok(inode, attr->ia_size); 35362306a36Sopenharmony_ci if (error) 35462306a36Sopenharmony_ci return error; 35562306a36Sopenharmony_ci truncate_setsize(inode, attr->ia_size); 35662306a36Sopenharmony_ci omfs_truncate(inode); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci setattr_copy(&nop_mnt_idmap, inode, attr); 36062306a36Sopenharmony_ci mark_inode_dirty(inode); 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ciconst struct inode_operations omfs_file_inops = { 36562306a36Sopenharmony_ci .setattr = omfs_setattr, 36662306a36Sopenharmony_ci}; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ciconst struct address_space_operations omfs_aops = { 36962306a36Sopenharmony_ci .dirty_folio = block_dirty_folio, 37062306a36Sopenharmony_ci .invalidate_folio = block_invalidate_folio, 37162306a36Sopenharmony_ci .read_folio = omfs_read_folio, 37262306a36Sopenharmony_ci .readahead = omfs_readahead, 37362306a36Sopenharmony_ci .writepages = omfs_writepages, 37462306a36Sopenharmony_ci .write_begin = omfs_write_begin, 37562306a36Sopenharmony_ci .write_end = generic_write_end, 37662306a36Sopenharmony_ci .bmap = omfs_bmap, 37762306a36Sopenharmony_ci .migrate_folio = buffer_migrate_folio, 37862306a36Sopenharmony_ci}; 37962306a36Sopenharmony_ci 380