162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/affs/file.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) 1996 Hans-Joachim Widmaier - Rewritten 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * (C) 1991 Linus Torvalds - minix filesystem 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * affs regular file handling primitives 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/uio.h> 1762306a36Sopenharmony_ci#include <linux/blkdev.h> 1862306a36Sopenharmony_ci#include <linux/mpage.h> 1962306a36Sopenharmony_ci#include "affs.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int 2462306a36Sopenharmony_ciaffs_file_open(struct inode *inode, struct file *filp) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci pr_debug("open(%lu,%d)\n", 2762306a36Sopenharmony_ci inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt)); 2862306a36Sopenharmony_ci atomic_inc(&AFFS_I(inode)->i_opencnt); 2962306a36Sopenharmony_ci return 0; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int 3362306a36Sopenharmony_ciaffs_file_release(struct inode *inode, struct file *filp) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci pr_debug("release(%lu, %d)\n", 3662306a36Sopenharmony_ci inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt)); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (atomic_dec_and_test(&AFFS_I(inode)->i_opencnt)) { 3962306a36Sopenharmony_ci inode_lock(inode); 4062306a36Sopenharmony_ci if (inode->i_size != AFFS_I(inode)->mmu_private) 4162306a36Sopenharmony_ci affs_truncate(inode); 4262306a36Sopenharmony_ci affs_free_prealloc(inode); 4362306a36Sopenharmony_ci inode_unlock(inode); 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int 5062306a36Sopenharmony_ciaffs_grow_extcache(struct inode *inode, u32 lc_idx) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 5362306a36Sopenharmony_ci struct buffer_head *bh; 5462306a36Sopenharmony_ci u32 lc_max; 5562306a36Sopenharmony_ci int i, j, key; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (!AFFS_I(inode)->i_lc) { 5862306a36Sopenharmony_ci char *ptr = (char *)get_zeroed_page(GFP_NOFS); 5962306a36Sopenharmony_ci if (!ptr) 6062306a36Sopenharmony_ci return -ENOMEM; 6162306a36Sopenharmony_ci AFFS_I(inode)->i_lc = (u32 *)ptr; 6262306a36Sopenharmony_ci AFFS_I(inode)->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci lc_max = AFFS_LC_SIZE << AFFS_I(inode)->i_lc_shift; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (AFFS_I(inode)->i_extcnt > lc_max) { 6862306a36Sopenharmony_ci u32 lc_shift, lc_mask, tmp, off; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* need to recalculate linear cache, start from old size */ 7162306a36Sopenharmony_ci lc_shift = AFFS_I(inode)->i_lc_shift; 7262306a36Sopenharmony_ci tmp = (AFFS_I(inode)->i_extcnt / AFFS_LC_SIZE) >> lc_shift; 7362306a36Sopenharmony_ci for (; tmp; tmp >>= 1) 7462306a36Sopenharmony_ci lc_shift++; 7562306a36Sopenharmony_ci lc_mask = (1 << lc_shift) - 1; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* fix idx and old size to new shift */ 7862306a36Sopenharmony_ci lc_idx >>= (lc_shift - AFFS_I(inode)->i_lc_shift); 7962306a36Sopenharmony_ci AFFS_I(inode)->i_lc_size >>= (lc_shift - AFFS_I(inode)->i_lc_shift); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* first shrink old cache to make more space */ 8262306a36Sopenharmony_ci off = 1 << (lc_shift - AFFS_I(inode)->i_lc_shift); 8362306a36Sopenharmony_ci for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off) 8462306a36Sopenharmony_ci AFFS_I(inode)->i_ac[i] = AFFS_I(inode)->i_ac[j]; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci AFFS_I(inode)->i_lc_shift = lc_shift; 8762306a36Sopenharmony_ci AFFS_I(inode)->i_lc_mask = lc_mask; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* fill cache to the needed index */ 9162306a36Sopenharmony_ci i = AFFS_I(inode)->i_lc_size; 9262306a36Sopenharmony_ci AFFS_I(inode)->i_lc_size = lc_idx + 1; 9362306a36Sopenharmony_ci for (; i <= lc_idx; i++) { 9462306a36Sopenharmony_ci if (!i) { 9562306a36Sopenharmony_ci AFFS_I(inode)->i_lc[0] = inode->i_ino; 9662306a36Sopenharmony_ci continue; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci key = AFFS_I(inode)->i_lc[i - 1]; 9962306a36Sopenharmony_ci j = AFFS_I(inode)->i_lc_mask + 1; 10062306a36Sopenharmony_ci // unlock cache 10162306a36Sopenharmony_ci for (; j > 0; j--) { 10262306a36Sopenharmony_ci bh = affs_bread(sb, key); 10362306a36Sopenharmony_ci if (!bh) 10462306a36Sopenharmony_ci goto err; 10562306a36Sopenharmony_ci key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); 10662306a36Sopenharmony_ci affs_brelse(bh); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci // lock cache 10962306a36Sopenharmony_ci AFFS_I(inode)->i_lc[i] = key; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cierr: 11562306a36Sopenharmony_ci // lock cache 11662306a36Sopenharmony_ci return -EIO; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic struct buffer_head * 12062306a36Sopenharmony_ciaffs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 12362306a36Sopenharmony_ci struct buffer_head *new_bh; 12462306a36Sopenharmony_ci u32 blocknr, tmp; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci blocknr = affs_alloc_block(inode, bh->b_blocknr); 12762306a36Sopenharmony_ci if (!blocknr) 12862306a36Sopenharmony_ci return ERR_PTR(-ENOSPC); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci new_bh = affs_getzeroblk(sb, blocknr); 13162306a36Sopenharmony_ci if (!new_bh) { 13262306a36Sopenharmony_ci affs_free_block(sb, blocknr); 13362306a36Sopenharmony_ci return ERR_PTR(-EIO); 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci AFFS_HEAD(new_bh)->ptype = cpu_to_be32(T_LIST); 13762306a36Sopenharmony_ci AFFS_HEAD(new_bh)->key = cpu_to_be32(blocknr); 13862306a36Sopenharmony_ci AFFS_TAIL(sb, new_bh)->stype = cpu_to_be32(ST_FILE); 13962306a36Sopenharmony_ci AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino); 14062306a36Sopenharmony_ci affs_fix_checksum(sb, new_bh); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci mark_buffer_dirty_inode(new_bh, inode); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); 14562306a36Sopenharmony_ci if (tmp) 14662306a36Sopenharmony_ci affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp); 14762306a36Sopenharmony_ci AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr); 14862306a36Sopenharmony_ci affs_adjust_checksum(bh, blocknr - tmp); 14962306a36Sopenharmony_ci mark_buffer_dirty_inode(bh, inode); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci AFFS_I(inode)->i_extcnt++; 15262306a36Sopenharmony_ci mark_inode_dirty(inode); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return new_bh; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic inline struct buffer_head * 15862306a36Sopenharmony_ciaffs_get_extblock(struct inode *inode, u32 ext) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci /* inline the simplest case: same extended block as last time */ 16162306a36Sopenharmony_ci struct buffer_head *bh = AFFS_I(inode)->i_ext_bh; 16262306a36Sopenharmony_ci if (ext == AFFS_I(inode)->i_ext_last) 16362306a36Sopenharmony_ci get_bh(bh); 16462306a36Sopenharmony_ci else 16562306a36Sopenharmony_ci /* we have to do more (not inlined) */ 16662306a36Sopenharmony_ci bh = affs_get_extblock_slow(inode, ext); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return bh; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct buffer_head * 17262306a36Sopenharmony_ciaffs_get_extblock_slow(struct inode *inode, u32 ext) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 17562306a36Sopenharmony_ci struct buffer_head *bh; 17662306a36Sopenharmony_ci u32 ext_key; 17762306a36Sopenharmony_ci u32 lc_idx, lc_off, ac_idx; 17862306a36Sopenharmony_ci u32 tmp, idx; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (ext == AFFS_I(inode)->i_ext_last + 1) { 18162306a36Sopenharmony_ci /* read the next extended block from the current one */ 18262306a36Sopenharmony_ci bh = AFFS_I(inode)->i_ext_bh; 18362306a36Sopenharmony_ci ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); 18462306a36Sopenharmony_ci if (ext < AFFS_I(inode)->i_extcnt) 18562306a36Sopenharmony_ci goto read_ext; 18662306a36Sopenharmony_ci BUG_ON(ext > AFFS_I(inode)->i_extcnt); 18762306a36Sopenharmony_ci bh = affs_alloc_extblock(inode, bh, ext); 18862306a36Sopenharmony_ci if (IS_ERR(bh)) 18962306a36Sopenharmony_ci return bh; 19062306a36Sopenharmony_ci goto store_ext; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (ext == 0) { 19462306a36Sopenharmony_ci /* we seek back to the file header block */ 19562306a36Sopenharmony_ci ext_key = inode->i_ino; 19662306a36Sopenharmony_ci goto read_ext; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (ext >= AFFS_I(inode)->i_extcnt) { 20062306a36Sopenharmony_ci struct buffer_head *prev_bh; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* allocate a new extended block */ 20362306a36Sopenharmony_ci BUG_ON(ext > AFFS_I(inode)->i_extcnt); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* get previous extended block */ 20662306a36Sopenharmony_ci prev_bh = affs_get_extblock(inode, ext - 1); 20762306a36Sopenharmony_ci if (IS_ERR(prev_bh)) 20862306a36Sopenharmony_ci return prev_bh; 20962306a36Sopenharmony_ci bh = affs_alloc_extblock(inode, prev_bh, ext); 21062306a36Sopenharmony_ci affs_brelse(prev_bh); 21162306a36Sopenharmony_ci if (IS_ERR(bh)) 21262306a36Sopenharmony_ci return bh; 21362306a36Sopenharmony_ci goto store_ext; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ciagain: 21762306a36Sopenharmony_ci /* check if there is an extended cache and whether it's large enough */ 21862306a36Sopenharmony_ci lc_idx = ext >> AFFS_I(inode)->i_lc_shift; 21962306a36Sopenharmony_ci lc_off = ext & AFFS_I(inode)->i_lc_mask; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (lc_idx >= AFFS_I(inode)->i_lc_size) { 22262306a36Sopenharmony_ci int err; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci err = affs_grow_extcache(inode, lc_idx); 22562306a36Sopenharmony_ci if (err) 22662306a36Sopenharmony_ci return ERR_PTR(err); 22762306a36Sopenharmony_ci goto again; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* every n'th key we find in the linear cache */ 23162306a36Sopenharmony_ci if (!lc_off) { 23262306a36Sopenharmony_ci ext_key = AFFS_I(inode)->i_lc[lc_idx]; 23362306a36Sopenharmony_ci goto read_ext; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* maybe it's still in the associative cache */ 23762306a36Sopenharmony_ci ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK; 23862306a36Sopenharmony_ci if (AFFS_I(inode)->i_ac[ac_idx].ext == ext) { 23962306a36Sopenharmony_ci ext_key = AFFS_I(inode)->i_ac[ac_idx].key; 24062306a36Sopenharmony_ci goto read_ext; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* try to find one of the previous extended blocks */ 24462306a36Sopenharmony_ci tmp = ext; 24562306a36Sopenharmony_ci idx = ac_idx; 24662306a36Sopenharmony_ci while (--tmp, --lc_off > 0) { 24762306a36Sopenharmony_ci idx = (idx - 1) & AFFS_AC_MASK; 24862306a36Sopenharmony_ci if (AFFS_I(inode)->i_ac[idx].ext == tmp) { 24962306a36Sopenharmony_ci ext_key = AFFS_I(inode)->i_ac[idx].key; 25062306a36Sopenharmony_ci goto find_ext; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* fall back to the linear cache */ 25562306a36Sopenharmony_ci ext_key = AFFS_I(inode)->i_lc[lc_idx]; 25662306a36Sopenharmony_cifind_ext: 25762306a36Sopenharmony_ci /* read all extended blocks until we find the one we need */ 25862306a36Sopenharmony_ci //unlock cache 25962306a36Sopenharmony_ci do { 26062306a36Sopenharmony_ci bh = affs_bread(sb, ext_key); 26162306a36Sopenharmony_ci if (!bh) 26262306a36Sopenharmony_ci goto err_bread; 26362306a36Sopenharmony_ci ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); 26462306a36Sopenharmony_ci affs_brelse(bh); 26562306a36Sopenharmony_ci tmp++; 26662306a36Sopenharmony_ci } while (tmp < ext); 26762306a36Sopenharmony_ci //lock cache 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* store it in the associative cache */ 27062306a36Sopenharmony_ci // recalculate ac_idx? 27162306a36Sopenharmony_ci AFFS_I(inode)->i_ac[ac_idx].ext = ext; 27262306a36Sopenharmony_ci AFFS_I(inode)->i_ac[ac_idx].key = ext_key; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ciread_ext: 27562306a36Sopenharmony_ci /* finally read the right extended block */ 27662306a36Sopenharmony_ci //unlock cache 27762306a36Sopenharmony_ci bh = affs_bread(sb, ext_key); 27862306a36Sopenharmony_ci if (!bh) 27962306a36Sopenharmony_ci goto err_bread; 28062306a36Sopenharmony_ci //lock cache 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistore_ext: 28362306a36Sopenharmony_ci /* release old cached extended block and store the new one */ 28462306a36Sopenharmony_ci affs_brelse(AFFS_I(inode)->i_ext_bh); 28562306a36Sopenharmony_ci AFFS_I(inode)->i_ext_last = ext; 28662306a36Sopenharmony_ci AFFS_I(inode)->i_ext_bh = bh; 28762306a36Sopenharmony_ci get_bh(bh); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return bh; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cierr_bread: 29262306a36Sopenharmony_ci affs_brelse(bh); 29362306a36Sopenharmony_ci return ERR_PTR(-EIO); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int 29762306a36Sopenharmony_ciaffs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 30062306a36Sopenharmony_ci struct buffer_head *ext_bh; 30162306a36Sopenharmony_ci u32 ext; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci pr_debug("%s(%lu, %llu)\n", __func__, inode->i_ino, 30462306a36Sopenharmony_ci (unsigned long long)block); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci BUG_ON(block > (sector_t)0x7fffffffUL); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (block >= AFFS_I(inode)->i_blkcnt) { 30962306a36Sopenharmony_ci if (block > AFFS_I(inode)->i_blkcnt || !create) 31062306a36Sopenharmony_ci goto err_big; 31162306a36Sopenharmony_ci } else 31262306a36Sopenharmony_ci create = 0; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci //lock cache 31562306a36Sopenharmony_ci affs_lock_ext(inode); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ext = (u32)block / AFFS_SB(sb)->s_hashsize; 31862306a36Sopenharmony_ci block -= ext * AFFS_SB(sb)->s_hashsize; 31962306a36Sopenharmony_ci ext_bh = affs_get_extblock(inode, ext); 32062306a36Sopenharmony_ci if (IS_ERR(ext_bh)) 32162306a36Sopenharmony_ci goto err_ext; 32262306a36Sopenharmony_ci map_bh(bh_result, sb, (sector_t)be32_to_cpu(AFFS_BLOCK(sb, ext_bh, block))); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (create) { 32562306a36Sopenharmony_ci u32 blocknr = affs_alloc_block(inode, ext_bh->b_blocknr); 32662306a36Sopenharmony_ci if (!blocknr) 32762306a36Sopenharmony_ci goto err_alloc; 32862306a36Sopenharmony_ci set_buffer_new(bh_result); 32962306a36Sopenharmony_ci AFFS_I(inode)->mmu_private += AFFS_SB(sb)->s_data_blksize; 33062306a36Sopenharmony_ci AFFS_I(inode)->i_blkcnt++; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* store new block */ 33362306a36Sopenharmony_ci if (bh_result->b_blocknr) 33462306a36Sopenharmony_ci affs_warning(sb, "get_block", 33562306a36Sopenharmony_ci "block already set (%llx)", 33662306a36Sopenharmony_ci (unsigned long long)bh_result->b_blocknr); 33762306a36Sopenharmony_ci AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr); 33862306a36Sopenharmony_ci AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1); 33962306a36Sopenharmony_ci affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1); 34062306a36Sopenharmony_ci bh_result->b_blocknr = blocknr; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (!block) { 34362306a36Sopenharmony_ci /* insert first block into header block */ 34462306a36Sopenharmony_ci u32 tmp = be32_to_cpu(AFFS_HEAD(ext_bh)->first_data); 34562306a36Sopenharmony_ci if (tmp) 34662306a36Sopenharmony_ci affs_warning(sb, "get_block", "first block already set (%d)", tmp); 34762306a36Sopenharmony_ci AFFS_HEAD(ext_bh)->first_data = cpu_to_be32(blocknr); 34862306a36Sopenharmony_ci affs_adjust_checksum(ext_bh, blocknr - tmp); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci affs_brelse(ext_bh); 35362306a36Sopenharmony_ci //unlock cache 35462306a36Sopenharmony_ci affs_unlock_ext(inode); 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cierr_big: 35862306a36Sopenharmony_ci affs_error(inode->i_sb, "get_block", "strange block request %llu", 35962306a36Sopenharmony_ci (unsigned long long)block); 36062306a36Sopenharmony_ci return -EIO; 36162306a36Sopenharmony_cierr_ext: 36262306a36Sopenharmony_ci // unlock cache 36362306a36Sopenharmony_ci affs_unlock_ext(inode); 36462306a36Sopenharmony_ci return PTR_ERR(ext_bh); 36562306a36Sopenharmony_cierr_alloc: 36662306a36Sopenharmony_ci brelse(ext_bh); 36762306a36Sopenharmony_ci clear_buffer_mapped(bh_result); 36862306a36Sopenharmony_ci bh_result->b_bdev = NULL; 36962306a36Sopenharmony_ci // unlock cache 37062306a36Sopenharmony_ci affs_unlock_ext(inode); 37162306a36Sopenharmony_ci return -ENOSPC; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int affs_writepages(struct address_space *mapping, 37562306a36Sopenharmony_ci struct writeback_control *wbc) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci return mpage_writepages(mapping, wbc, affs_get_block); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int affs_read_folio(struct file *file, struct folio *folio) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci return block_read_full_folio(folio, affs_get_block); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic void affs_write_failed(struct address_space *mapping, loff_t to) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct inode *inode = mapping->host; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (to > inode->i_size) { 39062306a36Sopenharmony_ci truncate_pagecache(inode, inode->i_size); 39162306a36Sopenharmony_ci affs_truncate(inode); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic ssize_t 39662306a36Sopenharmony_ciaffs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct file *file = iocb->ki_filp; 39962306a36Sopenharmony_ci struct address_space *mapping = file->f_mapping; 40062306a36Sopenharmony_ci struct inode *inode = mapping->host; 40162306a36Sopenharmony_ci size_t count = iov_iter_count(iter); 40262306a36Sopenharmony_ci loff_t offset = iocb->ki_pos; 40362306a36Sopenharmony_ci ssize_t ret; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (iov_iter_rw(iter) == WRITE) { 40662306a36Sopenharmony_ci loff_t size = offset + count; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (AFFS_I(inode)->mmu_private < size) 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci ret = blockdev_direct_IO(iocb, inode, iter, affs_get_block); 41362306a36Sopenharmony_ci if (ret < 0 && iov_iter_rw(iter) == WRITE) 41462306a36Sopenharmony_ci affs_write_failed(mapping, offset + count); 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int affs_write_begin(struct file *file, struct address_space *mapping, 41962306a36Sopenharmony_ci loff_t pos, unsigned len, 42062306a36Sopenharmony_ci struct page **pagep, void **fsdata) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci int ret; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci *pagep = NULL; 42562306a36Sopenharmony_ci ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata, 42662306a36Sopenharmony_ci affs_get_block, 42762306a36Sopenharmony_ci &AFFS_I(mapping->host)->mmu_private); 42862306a36Sopenharmony_ci if (unlikely(ret)) 42962306a36Sopenharmony_ci affs_write_failed(mapping, pos + len); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int affs_write_end(struct file *file, struct address_space *mapping, 43562306a36Sopenharmony_ci loff_t pos, unsigned int len, unsigned int copied, 43662306a36Sopenharmony_ci struct page *page, void *fsdata) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct inode *inode = mapping->host; 43962306a36Sopenharmony_ci int ret; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* Clear Archived bit on file writes, as AmigaOS would do */ 44462306a36Sopenharmony_ci if (AFFS_I(inode)->i_protect & FIBF_ARCHIVED) { 44562306a36Sopenharmony_ci AFFS_I(inode)->i_protect &= ~FIBF_ARCHIVED; 44662306a36Sopenharmony_ci mark_inode_dirty(inode); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return ret; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic sector_t _affs_bmap(struct address_space *mapping, sector_t block) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci return generic_block_bmap(mapping,block,affs_get_block); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ciconst struct address_space_operations affs_aops = { 45862306a36Sopenharmony_ci .dirty_folio = block_dirty_folio, 45962306a36Sopenharmony_ci .invalidate_folio = block_invalidate_folio, 46062306a36Sopenharmony_ci .read_folio = affs_read_folio, 46162306a36Sopenharmony_ci .writepages = affs_writepages, 46262306a36Sopenharmony_ci .write_begin = affs_write_begin, 46362306a36Sopenharmony_ci .write_end = affs_write_end, 46462306a36Sopenharmony_ci .direct_IO = affs_direct_IO, 46562306a36Sopenharmony_ci .migrate_folio = buffer_migrate_folio, 46662306a36Sopenharmony_ci .bmap = _affs_bmap 46762306a36Sopenharmony_ci}; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic inline struct buffer_head * 47062306a36Sopenharmony_ciaffs_bread_ino(struct inode *inode, int block, int create) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct buffer_head *bh, tmp_bh; 47362306a36Sopenharmony_ci int err; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci tmp_bh.b_state = 0; 47662306a36Sopenharmony_ci err = affs_get_block(inode, block, &tmp_bh, create); 47762306a36Sopenharmony_ci if (!err) { 47862306a36Sopenharmony_ci bh = affs_bread(inode->i_sb, tmp_bh.b_blocknr); 47962306a36Sopenharmony_ci if (bh) { 48062306a36Sopenharmony_ci bh->b_state |= tmp_bh.b_state; 48162306a36Sopenharmony_ci return bh; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci err = -EIO; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci return ERR_PTR(err); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic inline struct buffer_head * 48962306a36Sopenharmony_ciaffs_getzeroblk_ino(struct inode *inode, int block) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct buffer_head *bh, tmp_bh; 49262306a36Sopenharmony_ci int err; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci tmp_bh.b_state = 0; 49562306a36Sopenharmony_ci err = affs_get_block(inode, block, &tmp_bh, 1); 49662306a36Sopenharmony_ci if (!err) { 49762306a36Sopenharmony_ci bh = affs_getzeroblk(inode->i_sb, tmp_bh.b_blocknr); 49862306a36Sopenharmony_ci if (bh) { 49962306a36Sopenharmony_ci bh->b_state |= tmp_bh.b_state; 50062306a36Sopenharmony_ci return bh; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci err = -EIO; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci return ERR_PTR(err); 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic inline struct buffer_head * 50862306a36Sopenharmony_ciaffs_getemptyblk_ino(struct inode *inode, int block) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct buffer_head *bh, tmp_bh; 51162306a36Sopenharmony_ci int err; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci tmp_bh.b_state = 0; 51462306a36Sopenharmony_ci err = affs_get_block(inode, block, &tmp_bh, 1); 51562306a36Sopenharmony_ci if (!err) { 51662306a36Sopenharmony_ci bh = affs_getemptyblk(inode->i_sb, tmp_bh.b_blocknr); 51762306a36Sopenharmony_ci if (bh) { 51862306a36Sopenharmony_ci bh->b_state |= tmp_bh.b_state; 51962306a36Sopenharmony_ci return bh; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci err = -EIO; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci return ERR_PTR(err); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int affs_do_read_folio_ofs(struct folio *folio, size_t to, int create) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct inode *inode = folio->mapping->host; 52962306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 53062306a36Sopenharmony_ci struct buffer_head *bh; 53162306a36Sopenharmony_ci size_t pos = 0; 53262306a36Sopenharmony_ci size_t bidx, boff, bsize; 53362306a36Sopenharmony_ci u32 tmp; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci pr_debug("%s(%lu, %ld, 0, %zu)\n", __func__, inode->i_ino, 53662306a36Sopenharmony_ci folio->index, to); 53762306a36Sopenharmony_ci BUG_ON(to > folio_size(folio)); 53862306a36Sopenharmony_ci bsize = AFFS_SB(sb)->s_data_blksize; 53962306a36Sopenharmony_ci tmp = folio_pos(folio); 54062306a36Sopenharmony_ci bidx = tmp / bsize; 54162306a36Sopenharmony_ci boff = tmp % bsize; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci while (pos < to) { 54462306a36Sopenharmony_ci bh = affs_bread_ino(inode, bidx, create); 54562306a36Sopenharmony_ci if (IS_ERR(bh)) 54662306a36Sopenharmony_ci return PTR_ERR(bh); 54762306a36Sopenharmony_ci tmp = min(bsize - boff, to - pos); 54862306a36Sopenharmony_ci BUG_ON(pos + tmp > to || tmp > bsize); 54962306a36Sopenharmony_ci memcpy_to_folio(folio, pos, AFFS_DATA(bh) + boff, tmp); 55062306a36Sopenharmony_ci affs_brelse(bh); 55162306a36Sopenharmony_ci bidx++; 55262306a36Sopenharmony_ci pos += tmp; 55362306a36Sopenharmony_ci boff = 0; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int 55962306a36Sopenharmony_ciaffs_extent_file_ofs(struct inode *inode, u32 newsize) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 56262306a36Sopenharmony_ci struct buffer_head *bh, *prev_bh; 56362306a36Sopenharmony_ci u32 bidx, boff; 56462306a36Sopenharmony_ci u32 size, bsize; 56562306a36Sopenharmony_ci u32 tmp; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci pr_debug("%s(%lu, %d)\n", __func__, inode->i_ino, newsize); 56862306a36Sopenharmony_ci bsize = AFFS_SB(sb)->s_data_blksize; 56962306a36Sopenharmony_ci bh = NULL; 57062306a36Sopenharmony_ci size = AFFS_I(inode)->mmu_private; 57162306a36Sopenharmony_ci bidx = size / bsize; 57262306a36Sopenharmony_ci boff = size % bsize; 57362306a36Sopenharmony_ci if (boff) { 57462306a36Sopenharmony_ci bh = affs_bread_ino(inode, bidx, 0); 57562306a36Sopenharmony_ci if (IS_ERR(bh)) 57662306a36Sopenharmony_ci return PTR_ERR(bh); 57762306a36Sopenharmony_ci tmp = min(bsize - boff, newsize - size); 57862306a36Sopenharmony_ci BUG_ON(boff + tmp > bsize || tmp > bsize); 57962306a36Sopenharmony_ci memset(AFFS_DATA(bh) + boff, 0, tmp); 58062306a36Sopenharmony_ci be32_add_cpu(&AFFS_DATA_HEAD(bh)->size, tmp); 58162306a36Sopenharmony_ci affs_fix_checksum(sb, bh); 58262306a36Sopenharmony_ci mark_buffer_dirty_inode(bh, inode); 58362306a36Sopenharmony_ci size += tmp; 58462306a36Sopenharmony_ci bidx++; 58562306a36Sopenharmony_ci } else if (bidx) { 58662306a36Sopenharmony_ci bh = affs_bread_ino(inode, bidx - 1, 0); 58762306a36Sopenharmony_ci if (IS_ERR(bh)) 58862306a36Sopenharmony_ci return PTR_ERR(bh); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci while (size < newsize) { 59262306a36Sopenharmony_ci prev_bh = bh; 59362306a36Sopenharmony_ci bh = affs_getzeroblk_ino(inode, bidx); 59462306a36Sopenharmony_ci if (IS_ERR(bh)) 59562306a36Sopenharmony_ci goto out; 59662306a36Sopenharmony_ci tmp = min(bsize, newsize - size); 59762306a36Sopenharmony_ci BUG_ON(tmp > bsize); 59862306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA); 59962306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino); 60062306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx); 60162306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp); 60262306a36Sopenharmony_ci affs_fix_checksum(sb, bh); 60362306a36Sopenharmony_ci bh->b_state &= ~(1UL << BH_New); 60462306a36Sopenharmony_ci mark_buffer_dirty_inode(bh, inode); 60562306a36Sopenharmony_ci if (prev_bh) { 60662306a36Sopenharmony_ci u32 tmp_next = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (tmp_next) 60962306a36Sopenharmony_ci affs_warning(sb, "extent_file_ofs", 61062306a36Sopenharmony_ci "next block already set for %d (%d)", 61162306a36Sopenharmony_ci bidx, tmp_next); 61262306a36Sopenharmony_ci AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr); 61362306a36Sopenharmony_ci affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next); 61462306a36Sopenharmony_ci mark_buffer_dirty_inode(prev_bh, inode); 61562306a36Sopenharmony_ci affs_brelse(prev_bh); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci size += bsize; 61862306a36Sopenharmony_ci bidx++; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci affs_brelse(bh); 62162306a36Sopenharmony_ci inode->i_size = AFFS_I(inode)->mmu_private = newsize; 62262306a36Sopenharmony_ci return 0; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ciout: 62562306a36Sopenharmony_ci inode->i_size = AFFS_I(inode)->mmu_private = newsize; 62662306a36Sopenharmony_ci return PTR_ERR(bh); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int affs_read_folio_ofs(struct file *file, struct folio *folio) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci struct inode *inode = folio->mapping->host; 63262306a36Sopenharmony_ci size_t to; 63362306a36Sopenharmony_ci int err; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci pr_debug("%s(%lu, %ld)\n", __func__, inode->i_ino, folio->index); 63662306a36Sopenharmony_ci to = folio_size(folio); 63762306a36Sopenharmony_ci if (folio_pos(folio) + to > inode->i_size) { 63862306a36Sopenharmony_ci to = inode->i_size - folio_pos(folio); 63962306a36Sopenharmony_ci folio_zero_segment(folio, to, folio_size(folio)); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci err = affs_do_read_folio_ofs(folio, to, 0); 64362306a36Sopenharmony_ci if (!err) 64462306a36Sopenharmony_ci folio_mark_uptodate(folio); 64562306a36Sopenharmony_ci folio_unlock(folio); 64662306a36Sopenharmony_ci return err; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic int affs_write_begin_ofs(struct file *file, struct address_space *mapping, 65062306a36Sopenharmony_ci loff_t pos, unsigned len, 65162306a36Sopenharmony_ci struct page **pagep, void **fsdata) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct inode *inode = mapping->host; 65462306a36Sopenharmony_ci struct folio *folio; 65562306a36Sopenharmony_ci pgoff_t index; 65662306a36Sopenharmony_ci int err = 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci pr_debug("%s(%lu, %llu, %llu)\n", __func__, inode->i_ino, pos, 65962306a36Sopenharmony_ci pos + len); 66062306a36Sopenharmony_ci if (pos > AFFS_I(inode)->mmu_private) { 66162306a36Sopenharmony_ci /* XXX: this probably leaves a too-big i_size in case of 66262306a36Sopenharmony_ci * failure. Should really be updating i_size at write_end time 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci err = affs_extent_file_ofs(inode, pos); 66562306a36Sopenharmony_ci if (err) 66662306a36Sopenharmony_ci return err; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci index = pos >> PAGE_SHIFT; 67062306a36Sopenharmony_ci folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN, 67162306a36Sopenharmony_ci mapping_gfp_mask(mapping)); 67262306a36Sopenharmony_ci if (IS_ERR(folio)) 67362306a36Sopenharmony_ci return PTR_ERR(folio); 67462306a36Sopenharmony_ci *pagep = &folio->page; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (folio_test_uptodate(folio)) 67762306a36Sopenharmony_ci return 0; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* XXX: inefficient but safe in the face of short writes */ 68062306a36Sopenharmony_ci err = affs_do_read_folio_ofs(folio, folio_size(folio), 1); 68162306a36Sopenharmony_ci if (err) { 68262306a36Sopenharmony_ci folio_unlock(folio); 68362306a36Sopenharmony_ci folio_put(folio); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci return err; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int affs_write_end_ofs(struct file *file, struct address_space *mapping, 68962306a36Sopenharmony_ci loff_t pos, unsigned len, unsigned copied, 69062306a36Sopenharmony_ci struct page *page, void *fsdata) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct folio *folio = page_folio(page); 69362306a36Sopenharmony_ci struct inode *inode = mapping->host; 69462306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 69562306a36Sopenharmony_ci struct buffer_head *bh, *prev_bh; 69662306a36Sopenharmony_ci char *data; 69762306a36Sopenharmony_ci u32 bidx, boff, bsize; 69862306a36Sopenharmony_ci unsigned from, to; 69962306a36Sopenharmony_ci u32 tmp; 70062306a36Sopenharmony_ci int written; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci from = pos & (PAGE_SIZE - 1); 70362306a36Sopenharmony_ci to = from + len; 70462306a36Sopenharmony_ci /* 70562306a36Sopenharmony_ci * XXX: not sure if this can handle short copies (len < copied), but 70662306a36Sopenharmony_ci * we don't have to, because the folio should always be uptodate here, 70762306a36Sopenharmony_ci * due to write_begin. 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci pr_debug("%s(%lu, %llu, %llu)\n", __func__, inode->i_ino, pos, 71162306a36Sopenharmony_ci pos + len); 71262306a36Sopenharmony_ci bsize = AFFS_SB(sb)->s_data_blksize; 71362306a36Sopenharmony_ci data = folio_address(folio); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci bh = NULL; 71662306a36Sopenharmony_ci written = 0; 71762306a36Sopenharmony_ci tmp = (folio->index << PAGE_SHIFT) + from; 71862306a36Sopenharmony_ci bidx = tmp / bsize; 71962306a36Sopenharmony_ci boff = tmp % bsize; 72062306a36Sopenharmony_ci if (boff) { 72162306a36Sopenharmony_ci bh = affs_bread_ino(inode, bidx, 0); 72262306a36Sopenharmony_ci if (IS_ERR(bh)) { 72362306a36Sopenharmony_ci written = PTR_ERR(bh); 72462306a36Sopenharmony_ci goto err_first_bh; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci tmp = min(bsize - boff, to - from); 72762306a36Sopenharmony_ci BUG_ON(boff + tmp > bsize || tmp > bsize); 72862306a36Sopenharmony_ci memcpy(AFFS_DATA(bh) + boff, data + from, tmp); 72962306a36Sopenharmony_ci be32_add_cpu(&AFFS_DATA_HEAD(bh)->size, tmp); 73062306a36Sopenharmony_ci affs_fix_checksum(sb, bh); 73162306a36Sopenharmony_ci mark_buffer_dirty_inode(bh, inode); 73262306a36Sopenharmony_ci written += tmp; 73362306a36Sopenharmony_ci from += tmp; 73462306a36Sopenharmony_ci bidx++; 73562306a36Sopenharmony_ci } else if (bidx) { 73662306a36Sopenharmony_ci bh = affs_bread_ino(inode, bidx - 1, 0); 73762306a36Sopenharmony_ci if (IS_ERR(bh)) { 73862306a36Sopenharmony_ci written = PTR_ERR(bh); 73962306a36Sopenharmony_ci goto err_first_bh; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci while (from + bsize <= to) { 74362306a36Sopenharmony_ci prev_bh = bh; 74462306a36Sopenharmony_ci bh = affs_getemptyblk_ino(inode, bidx); 74562306a36Sopenharmony_ci if (IS_ERR(bh)) 74662306a36Sopenharmony_ci goto err_bh; 74762306a36Sopenharmony_ci memcpy(AFFS_DATA(bh), data + from, bsize); 74862306a36Sopenharmony_ci if (buffer_new(bh)) { 74962306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA); 75062306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino); 75162306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx); 75262306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->size = cpu_to_be32(bsize); 75362306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->next = 0; 75462306a36Sopenharmony_ci bh->b_state &= ~(1UL << BH_New); 75562306a36Sopenharmony_ci if (prev_bh) { 75662306a36Sopenharmony_ci u32 tmp_next = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (tmp_next) 75962306a36Sopenharmony_ci affs_warning(sb, "commit_write_ofs", 76062306a36Sopenharmony_ci "next block already set for %d (%d)", 76162306a36Sopenharmony_ci bidx, tmp_next); 76262306a36Sopenharmony_ci AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr); 76362306a36Sopenharmony_ci affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next); 76462306a36Sopenharmony_ci mark_buffer_dirty_inode(prev_bh, inode); 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci affs_brelse(prev_bh); 76862306a36Sopenharmony_ci affs_fix_checksum(sb, bh); 76962306a36Sopenharmony_ci mark_buffer_dirty_inode(bh, inode); 77062306a36Sopenharmony_ci written += bsize; 77162306a36Sopenharmony_ci from += bsize; 77262306a36Sopenharmony_ci bidx++; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci if (from < to) { 77562306a36Sopenharmony_ci prev_bh = bh; 77662306a36Sopenharmony_ci bh = affs_bread_ino(inode, bidx, 1); 77762306a36Sopenharmony_ci if (IS_ERR(bh)) 77862306a36Sopenharmony_ci goto err_bh; 77962306a36Sopenharmony_ci tmp = min(bsize, to - from); 78062306a36Sopenharmony_ci BUG_ON(tmp > bsize); 78162306a36Sopenharmony_ci memcpy(AFFS_DATA(bh), data + from, tmp); 78262306a36Sopenharmony_ci if (buffer_new(bh)) { 78362306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA); 78462306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino); 78562306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx); 78662306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp); 78762306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->next = 0; 78862306a36Sopenharmony_ci bh->b_state &= ~(1UL << BH_New); 78962306a36Sopenharmony_ci if (prev_bh) { 79062306a36Sopenharmony_ci u32 tmp_next = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (tmp_next) 79362306a36Sopenharmony_ci affs_warning(sb, "commit_write_ofs", 79462306a36Sopenharmony_ci "next block already set for %d (%d)", 79562306a36Sopenharmony_ci bidx, tmp_next); 79662306a36Sopenharmony_ci AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr); 79762306a36Sopenharmony_ci affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next); 79862306a36Sopenharmony_ci mark_buffer_dirty_inode(prev_bh, inode); 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci } else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp) 80162306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp); 80262306a36Sopenharmony_ci affs_brelse(prev_bh); 80362306a36Sopenharmony_ci affs_fix_checksum(sb, bh); 80462306a36Sopenharmony_ci mark_buffer_dirty_inode(bh, inode); 80562306a36Sopenharmony_ci written += tmp; 80662306a36Sopenharmony_ci from += tmp; 80762306a36Sopenharmony_ci bidx++; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci folio_mark_uptodate(folio); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cidone: 81262306a36Sopenharmony_ci affs_brelse(bh); 81362306a36Sopenharmony_ci tmp = (folio->index << PAGE_SHIFT) + from; 81462306a36Sopenharmony_ci if (tmp > inode->i_size) 81562306a36Sopenharmony_ci inode->i_size = AFFS_I(inode)->mmu_private = tmp; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* Clear Archived bit on file writes, as AmigaOS would do */ 81862306a36Sopenharmony_ci if (AFFS_I(inode)->i_protect & FIBF_ARCHIVED) { 81962306a36Sopenharmony_ci AFFS_I(inode)->i_protect &= ~FIBF_ARCHIVED; 82062306a36Sopenharmony_ci mark_inode_dirty(inode); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cierr_first_bh: 82462306a36Sopenharmony_ci folio_unlock(folio); 82562306a36Sopenharmony_ci folio_put(folio); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci return written; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cierr_bh: 83062306a36Sopenharmony_ci bh = prev_bh; 83162306a36Sopenharmony_ci if (!written) 83262306a36Sopenharmony_ci written = PTR_ERR(bh); 83362306a36Sopenharmony_ci goto done; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ciconst struct address_space_operations affs_aops_ofs = { 83762306a36Sopenharmony_ci .dirty_folio = block_dirty_folio, 83862306a36Sopenharmony_ci .invalidate_folio = block_invalidate_folio, 83962306a36Sopenharmony_ci .read_folio = affs_read_folio_ofs, 84062306a36Sopenharmony_ci //.writepages = affs_writepages_ofs, 84162306a36Sopenharmony_ci .write_begin = affs_write_begin_ofs, 84262306a36Sopenharmony_ci .write_end = affs_write_end_ofs, 84362306a36Sopenharmony_ci .migrate_folio = filemap_migrate_folio, 84462306a36Sopenharmony_ci}; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci/* Free any preallocated blocks. */ 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_civoid 84962306a36Sopenharmony_ciaffs_free_prealloc(struct inode *inode) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci pr_debug("free_prealloc(ino=%lu)\n", inode->i_ino); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci while (AFFS_I(inode)->i_pa_cnt) { 85662306a36Sopenharmony_ci AFFS_I(inode)->i_pa_cnt--; 85762306a36Sopenharmony_ci affs_free_block(sb, ++AFFS_I(inode)->i_lastalloc); 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci/* Truncate (or enlarge) a file to the requested size. */ 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_civoid 86462306a36Sopenharmony_ciaffs_truncate(struct inode *inode) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 86762306a36Sopenharmony_ci u32 ext, ext_key; 86862306a36Sopenharmony_ci u32 last_blk, blkcnt, blk; 86962306a36Sopenharmony_ci u32 size; 87062306a36Sopenharmony_ci struct buffer_head *ext_bh; 87162306a36Sopenharmony_ci int i; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci pr_debug("truncate(inode=%lu, oldsize=%llu, newsize=%llu)\n", 87462306a36Sopenharmony_ci inode->i_ino, AFFS_I(inode)->mmu_private, inode->i_size); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci last_blk = 0; 87762306a36Sopenharmony_ci ext = 0; 87862306a36Sopenharmony_ci if (inode->i_size) { 87962306a36Sopenharmony_ci last_blk = ((u32)inode->i_size - 1) / AFFS_SB(sb)->s_data_blksize; 88062306a36Sopenharmony_ci ext = last_blk / AFFS_SB(sb)->s_hashsize; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (inode->i_size > AFFS_I(inode)->mmu_private) { 88462306a36Sopenharmony_ci struct address_space *mapping = inode->i_mapping; 88562306a36Sopenharmony_ci struct page *page; 88662306a36Sopenharmony_ci void *fsdata = NULL; 88762306a36Sopenharmony_ci loff_t isize = inode->i_size; 88862306a36Sopenharmony_ci int res; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci res = mapping->a_ops->write_begin(NULL, mapping, isize, 0, &page, &fsdata); 89162306a36Sopenharmony_ci if (!res) 89262306a36Sopenharmony_ci res = mapping->a_ops->write_end(NULL, mapping, isize, 0, 0, page, fsdata); 89362306a36Sopenharmony_ci else 89462306a36Sopenharmony_ci inode->i_size = AFFS_I(inode)->mmu_private; 89562306a36Sopenharmony_ci mark_inode_dirty(inode); 89662306a36Sopenharmony_ci return; 89762306a36Sopenharmony_ci } else if (inode->i_size == AFFS_I(inode)->mmu_private) 89862306a36Sopenharmony_ci return; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci // lock cache 90162306a36Sopenharmony_ci ext_bh = affs_get_extblock(inode, ext); 90262306a36Sopenharmony_ci if (IS_ERR(ext_bh)) { 90362306a36Sopenharmony_ci affs_warning(sb, "truncate", 90462306a36Sopenharmony_ci "unexpected read error for ext block %u (%ld)", 90562306a36Sopenharmony_ci ext, PTR_ERR(ext_bh)); 90662306a36Sopenharmony_ci return; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci if (AFFS_I(inode)->i_lc) { 90962306a36Sopenharmony_ci /* clear linear cache */ 91062306a36Sopenharmony_ci i = (ext + 1) >> AFFS_I(inode)->i_lc_shift; 91162306a36Sopenharmony_ci if (AFFS_I(inode)->i_lc_size > i) { 91262306a36Sopenharmony_ci AFFS_I(inode)->i_lc_size = i; 91362306a36Sopenharmony_ci for (; i < AFFS_LC_SIZE; i++) 91462306a36Sopenharmony_ci AFFS_I(inode)->i_lc[i] = 0; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci /* clear associative cache */ 91762306a36Sopenharmony_ci for (i = 0; i < AFFS_AC_SIZE; i++) 91862306a36Sopenharmony_ci if (AFFS_I(inode)->i_ac[i].ext >= ext) 91962306a36Sopenharmony_ci AFFS_I(inode)->i_ac[i].ext = 0; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci blkcnt = AFFS_I(inode)->i_blkcnt; 92462306a36Sopenharmony_ci i = 0; 92562306a36Sopenharmony_ci blk = last_blk; 92662306a36Sopenharmony_ci if (inode->i_size) { 92762306a36Sopenharmony_ci i = last_blk % AFFS_SB(sb)->s_hashsize + 1; 92862306a36Sopenharmony_ci blk++; 92962306a36Sopenharmony_ci } else 93062306a36Sopenharmony_ci AFFS_HEAD(ext_bh)->first_data = 0; 93162306a36Sopenharmony_ci AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(i); 93262306a36Sopenharmony_ci size = AFFS_SB(sb)->s_hashsize; 93362306a36Sopenharmony_ci if (size > blkcnt - blk + i) 93462306a36Sopenharmony_ci size = blkcnt - blk + i; 93562306a36Sopenharmony_ci for (; i < size; i++, blk++) { 93662306a36Sopenharmony_ci affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i))); 93762306a36Sopenharmony_ci AFFS_BLOCK(sb, ext_bh, i) = 0; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci AFFS_TAIL(sb, ext_bh)->extension = 0; 94062306a36Sopenharmony_ci affs_fix_checksum(sb, ext_bh); 94162306a36Sopenharmony_ci mark_buffer_dirty_inode(ext_bh, inode); 94262306a36Sopenharmony_ci affs_brelse(ext_bh); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (inode->i_size) { 94562306a36Sopenharmony_ci AFFS_I(inode)->i_blkcnt = last_blk + 1; 94662306a36Sopenharmony_ci AFFS_I(inode)->i_extcnt = ext + 1; 94762306a36Sopenharmony_ci if (affs_test_opt(AFFS_SB(sb)->s_flags, SF_OFS)) { 94862306a36Sopenharmony_ci struct buffer_head *bh = affs_bread_ino(inode, last_blk, 0); 94962306a36Sopenharmony_ci u32 tmp; 95062306a36Sopenharmony_ci if (IS_ERR(bh)) { 95162306a36Sopenharmony_ci affs_warning(sb, "truncate", 95262306a36Sopenharmony_ci "unexpected read error for last block %u (%ld)", 95362306a36Sopenharmony_ci ext, PTR_ERR(bh)); 95462306a36Sopenharmony_ci return; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci tmp = be32_to_cpu(AFFS_DATA_HEAD(bh)->next); 95762306a36Sopenharmony_ci AFFS_DATA_HEAD(bh)->next = 0; 95862306a36Sopenharmony_ci affs_adjust_checksum(bh, -tmp); 95962306a36Sopenharmony_ci affs_brelse(bh); 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci } else { 96262306a36Sopenharmony_ci AFFS_I(inode)->i_blkcnt = 0; 96362306a36Sopenharmony_ci AFFS_I(inode)->i_extcnt = 1; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci AFFS_I(inode)->mmu_private = inode->i_size; 96662306a36Sopenharmony_ci // unlock cache 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci while (ext_key) { 96962306a36Sopenharmony_ci ext_bh = affs_bread(sb, ext_key); 97062306a36Sopenharmony_ci size = AFFS_SB(sb)->s_hashsize; 97162306a36Sopenharmony_ci if (size > blkcnt - blk) 97262306a36Sopenharmony_ci size = blkcnt - blk; 97362306a36Sopenharmony_ci for (i = 0; i < size; i++, blk++) 97462306a36Sopenharmony_ci affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i))); 97562306a36Sopenharmony_ci affs_free_block(sb, ext_key); 97662306a36Sopenharmony_ci ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension); 97762306a36Sopenharmony_ci affs_brelse(ext_bh); 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci affs_free_prealloc(inode); 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ciint affs_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct inode *inode = filp->f_mapping->host; 98562306a36Sopenharmony_ci int ret, err; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci err = file_write_and_wait_range(filp, start, end); 98862306a36Sopenharmony_ci if (err) 98962306a36Sopenharmony_ci return err; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci inode_lock(inode); 99262306a36Sopenharmony_ci ret = write_inode_now(inode, 0); 99362306a36Sopenharmony_ci err = sync_blockdev(inode->i_sb->s_bdev); 99462306a36Sopenharmony_ci if (!ret) 99562306a36Sopenharmony_ci ret = err; 99662306a36Sopenharmony_ci inode_unlock(inode); 99762306a36Sopenharmony_ci return ret; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ciconst struct file_operations affs_file_operations = { 100062306a36Sopenharmony_ci .llseek = generic_file_llseek, 100162306a36Sopenharmony_ci .read_iter = generic_file_read_iter, 100262306a36Sopenharmony_ci .write_iter = generic_file_write_iter, 100362306a36Sopenharmony_ci .mmap = generic_file_mmap, 100462306a36Sopenharmony_ci .open = affs_file_open, 100562306a36Sopenharmony_ci .release = affs_file_release, 100662306a36Sopenharmony_ci .fsync = affs_file_fsync, 100762306a36Sopenharmony_ci .splice_read = filemap_splice_read, 100862306a36Sopenharmony_ci}; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ciconst struct inode_operations affs_file_inode_operations = { 101162306a36Sopenharmony_ci .setattr = affs_notify_change, 101262306a36Sopenharmony_ci}; 1013