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