162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/ext2/file.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1992, 1993, 1994, 1995
662306a36Sopenharmony_ci * Remy Card (card@masi.ibp.fr)
762306a36Sopenharmony_ci * Laboratoire MASI - Institut Blaise Pascal
862306a36Sopenharmony_ci * Universite Pierre et Marie Curie (Paris VI)
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *  from
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *  linux/fs/minix/file.c
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *  Copyright (C) 1991, 1992  Linus Torvalds
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *  ext2 fs regular file handling primitives
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *  64-bit file support on 64-bit platforms by Jakub Jelinek
1962306a36Sopenharmony_ci * 	(jj@sunsite.ms.mff.cuni.cz)
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/time.h>
2362306a36Sopenharmony_ci#include <linux/pagemap.h>
2462306a36Sopenharmony_ci#include <linux/dax.h>
2562306a36Sopenharmony_ci#include <linux/quotaops.h>
2662306a36Sopenharmony_ci#include <linux/iomap.h>
2762306a36Sopenharmony_ci#include <linux/uio.h>
2862306a36Sopenharmony_ci#include <linux/buffer_head.h>
2962306a36Sopenharmony_ci#include "ext2.h"
3062306a36Sopenharmony_ci#include "xattr.h"
3162306a36Sopenharmony_ci#include "acl.h"
3262306a36Sopenharmony_ci#include "trace.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#ifdef CONFIG_FS_DAX
3562306a36Sopenharmony_cistatic ssize_t ext2_dax_read_iter(struct kiocb *iocb, struct iov_iter *to)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct inode *inode = iocb->ki_filp->f_mapping->host;
3862306a36Sopenharmony_ci	ssize_t ret;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (!iov_iter_count(to))
4162306a36Sopenharmony_ci		return 0; /* skip atime */
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	inode_lock_shared(inode);
4462306a36Sopenharmony_ci	ret = dax_iomap_rw(iocb, to, &ext2_iomap_ops);
4562306a36Sopenharmony_ci	inode_unlock_shared(inode);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	file_accessed(iocb->ki_filp);
4862306a36Sopenharmony_ci	return ret;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic ssize_t ext2_dax_write_iter(struct kiocb *iocb, struct iov_iter *from)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct file *file = iocb->ki_filp;
5462306a36Sopenharmony_ci	struct inode *inode = file->f_mapping->host;
5562306a36Sopenharmony_ci	ssize_t ret;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	inode_lock(inode);
5862306a36Sopenharmony_ci	ret = generic_write_checks(iocb, from);
5962306a36Sopenharmony_ci	if (ret <= 0)
6062306a36Sopenharmony_ci		goto out_unlock;
6162306a36Sopenharmony_ci	ret = file_remove_privs(file);
6262306a36Sopenharmony_ci	if (ret)
6362306a36Sopenharmony_ci		goto out_unlock;
6462306a36Sopenharmony_ci	ret = file_update_time(file);
6562306a36Sopenharmony_ci	if (ret)
6662306a36Sopenharmony_ci		goto out_unlock;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ret = dax_iomap_rw(iocb, from, &ext2_iomap_ops);
6962306a36Sopenharmony_ci	if (ret > 0 && iocb->ki_pos > i_size_read(inode)) {
7062306a36Sopenharmony_ci		i_size_write(inode, iocb->ki_pos);
7162306a36Sopenharmony_ci		mark_inode_dirty(inode);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciout_unlock:
7562306a36Sopenharmony_ci	inode_unlock(inode);
7662306a36Sopenharmony_ci	if (ret > 0)
7762306a36Sopenharmony_ci		ret = generic_write_sync(iocb, ret);
7862306a36Sopenharmony_ci	return ret;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/*
8262306a36Sopenharmony_ci * The lock ordering for ext2 DAX fault paths is:
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci * mmap_lock (MM)
8562306a36Sopenharmony_ci *   sb_start_pagefault (vfs, freeze)
8662306a36Sopenharmony_ci *     address_space->invalidate_lock
8762306a36Sopenharmony_ci *       address_space->i_mmap_rwsem or page_lock (mutually exclusive in DAX)
8862306a36Sopenharmony_ci *         ext2_inode_info->truncate_mutex
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * The default page_lock and i_size verification done by non-DAX fault paths
9162306a36Sopenharmony_ci * is sufficient because ext2 doesn't support hole punching.
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_cistatic vm_fault_t ext2_dax_fault(struct vm_fault *vmf)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct inode *inode = file_inode(vmf->vma->vm_file);
9662306a36Sopenharmony_ci	vm_fault_t ret;
9762306a36Sopenharmony_ci	bool write = (vmf->flags & FAULT_FLAG_WRITE) &&
9862306a36Sopenharmony_ci		(vmf->vma->vm_flags & VM_SHARED);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (write) {
10162306a36Sopenharmony_ci		sb_start_pagefault(inode->i_sb);
10262306a36Sopenharmony_ci		file_update_time(vmf->vma->vm_file);
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	filemap_invalidate_lock_shared(inode->i_mapping);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	ret = dax_iomap_fault(vmf, 0, NULL, NULL, &ext2_iomap_ops);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	filemap_invalidate_unlock_shared(inode->i_mapping);
10962306a36Sopenharmony_ci	if (write)
11062306a36Sopenharmony_ci		sb_end_pagefault(inode->i_sb);
11162306a36Sopenharmony_ci	return ret;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic const struct vm_operations_struct ext2_dax_vm_ops = {
11562306a36Sopenharmony_ci	.fault		= ext2_dax_fault,
11662306a36Sopenharmony_ci	/*
11762306a36Sopenharmony_ci	 * .huge_fault is not supported for DAX because allocation in ext2
11862306a36Sopenharmony_ci	 * cannot be reliably aligned to huge page sizes and so pmd faults
11962306a36Sopenharmony_ci	 * will always fail and fail back to regular faults.
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	.page_mkwrite	= ext2_dax_fault,
12262306a36Sopenharmony_ci	.pfn_mkwrite	= ext2_dax_fault,
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int ext2_file_mmap(struct file *file, struct vm_area_struct *vma)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	if (!IS_DAX(file_inode(file)))
12862306a36Sopenharmony_ci		return generic_file_mmap(file, vma);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	file_accessed(file);
13162306a36Sopenharmony_ci	vma->vm_ops = &ext2_dax_vm_ops;
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci#else
13562306a36Sopenharmony_ci#define ext2_file_mmap	generic_file_mmap
13662306a36Sopenharmony_ci#endif
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/*
13962306a36Sopenharmony_ci * Called when filp is released. This happens when all file descriptors
14062306a36Sopenharmony_ci * for a single struct file are closed. Note that different open() calls
14162306a36Sopenharmony_ci * for the same file yield different struct file structures.
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_cistatic int ext2_release_file (struct inode * inode, struct file * filp)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	if (filp->f_mode & FMODE_WRITE) {
14662306a36Sopenharmony_ci		mutex_lock(&EXT2_I(inode)->truncate_mutex);
14762306a36Sopenharmony_ci		ext2_discard_reservation(inode);
14862306a36Sopenharmony_ci		mutex_unlock(&EXT2_I(inode)->truncate_mutex);
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ciint ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	int ret;
15662306a36Sopenharmony_ci	struct super_block *sb = file->f_mapping->host->i_sb;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	ret = generic_buffers_fsync(file, start, end, datasync);
15962306a36Sopenharmony_ci	if (ret == -EIO)
16062306a36Sopenharmony_ci		/* We don't really know where the IO error happened... */
16162306a36Sopenharmony_ci		ext2_error(sb, __func__,
16262306a36Sopenharmony_ci			   "detected IO error when writing metadata buffers");
16362306a36Sopenharmony_ci	return ret;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic ssize_t ext2_dio_read_iter(struct kiocb *iocb, struct iov_iter *to)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct file *file = iocb->ki_filp;
16962306a36Sopenharmony_ci	struct inode *inode = file->f_mapping->host;
17062306a36Sopenharmony_ci	ssize_t ret;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	trace_ext2_dio_read_begin(iocb, to, 0);
17362306a36Sopenharmony_ci	inode_lock_shared(inode);
17462306a36Sopenharmony_ci	ret = iomap_dio_rw(iocb, to, &ext2_iomap_ops, NULL, 0, NULL, 0);
17562306a36Sopenharmony_ci	inode_unlock_shared(inode);
17662306a36Sopenharmony_ci	trace_ext2_dio_read_end(iocb, to, ret);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return ret;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int ext2_dio_write_end_io(struct kiocb *iocb, ssize_t size,
18262306a36Sopenharmony_ci				 int error, unsigned int flags)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	loff_t pos = iocb->ki_pos;
18562306a36Sopenharmony_ci	struct inode *inode = file_inode(iocb->ki_filp);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (error)
18862306a36Sopenharmony_ci		goto out;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/*
19162306a36Sopenharmony_ci	 * If we are extending the file, we have to update i_size here before
19262306a36Sopenharmony_ci	 * page cache gets invalidated in iomap_dio_rw(). This prevents racing
19362306a36Sopenharmony_ci	 * buffered reads from zeroing out too much from page cache pages.
19462306a36Sopenharmony_ci	 * Note that all extending writes always happens synchronously with
19562306a36Sopenharmony_ci	 * inode lock held by ext2_dio_write_iter(). So it is safe to update
19662306a36Sopenharmony_ci	 * inode size here for extending file writes.
19762306a36Sopenharmony_ci	 */
19862306a36Sopenharmony_ci	pos += size;
19962306a36Sopenharmony_ci	if (pos > i_size_read(inode)) {
20062306a36Sopenharmony_ci		i_size_write(inode, pos);
20162306a36Sopenharmony_ci		mark_inode_dirty(inode);
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ciout:
20462306a36Sopenharmony_ci	trace_ext2_dio_write_endio(iocb, size, error);
20562306a36Sopenharmony_ci	return error;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic const struct iomap_dio_ops ext2_dio_write_ops = {
20962306a36Sopenharmony_ci	.end_io = ext2_dio_write_end_io,
21062306a36Sopenharmony_ci};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic ssize_t ext2_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct file *file = iocb->ki_filp;
21562306a36Sopenharmony_ci	struct inode *inode = file->f_mapping->host;
21662306a36Sopenharmony_ci	ssize_t ret;
21762306a36Sopenharmony_ci	unsigned int flags = 0;
21862306a36Sopenharmony_ci	unsigned long blocksize = inode->i_sb->s_blocksize;
21962306a36Sopenharmony_ci	loff_t offset = iocb->ki_pos;
22062306a36Sopenharmony_ci	loff_t count = iov_iter_count(from);
22162306a36Sopenharmony_ci	ssize_t status = 0;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	trace_ext2_dio_write_begin(iocb, from, 0);
22462306a36Sopenharmony_ci	inode_lock(inode);
22562306a36Sopenharmony_ci	ret = generic_write_checks(iocb, from);
22662306a36Sopenharmony_ci	if (ret <= 0)
22762306a36Sopenharmony_ci		goto out_unlock;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	ret = kiocb_modified(iocb);
23062306a36Sopenharmony_ci	if (ret)
23162306a36Sopenharmony_ci		goto out_unlock;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* use IOMAP_DIO_FORCE_WAIT for unaligned or extending writes */
23462306a36Sopenharmony_ci	if (iocb->ki_pos + iov_iter_count(from) > i_size_read(inode) ||
23562306a36Sopenharmony_ci	   (!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(from), blocksize)))
23662306a36Sopenharmony_ci		flags |= IOMAP_DIO_FORCE_WAIT;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	ret = iomap_dio_rw(iocb, from, &ext2_iomap_ops, &ext2_dio_write_ops,
23962306a36Sopenharmony_ci			   flags, NULL, 0);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* ENOTBLK is magic return value for fallback to buffered-io */
24262306a36Sopenharmony_ci	if (ret == -ENOTBLK)
24362306a36Sopenharmony_ci		ret = 0;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (ret < 0 && ret != -EIOCBQUEUED)
24662306a36Sopenharmony_ci		ext2_write_failed(inode->i_mapping, offset + count);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* handle case for partial write and for fallback to buffered write */
24962306a36Sopenharmony_ci	if (ret >= 0 && iov_iter_count(from)) {
25062306a36Sopenharmony_ci		loff_t pos, endbyte;
25162306a36Sopenharmony_ci		int ret2;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		iocb->ki_flags &= ~IOCB_DIRECT;
25462306a36Sopenharmony_ci		pos = iocb->ki_pos;
25562306a36Sopenharmony_ci		status = generic_perform_write(iocb, from);
25662306a36Sopenharmony_ci		if (unlikely(status < 0)) {
25762306a36Sopenharmony_ci			ret = status;
25862306a36Sopenharmony_ci			goto out_unlock;
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		ret += status;
26262306a36Sopenharmony_ci		endbyte = pos + status - 1;
26362306a36Sopenharmony_ci		ret2 = filemap_write_and_wait_range(inode->i_mapping, pos,
26462306a36Sopenharmony_ci						    endbyte);
26562306a36Sopenharmony_ci		if (!ret2)
26662306a36Sopenharmony_ci			invalidate_mapping_pages(inode->i_mapping,
26762306a36Sopenharmony_ci						 pos >> PAGE_SHIFT,
26862306a36Sopenharmony_ci						 endbyte >> PAGE_SHIFT);
26962306a36Sopenharmony_ci		if (ret > 0)
27062306a36Sopenharmony_ci			generic_write_sync(iocb, ret);
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ciout_unlock:
27462306a36Sopenharmony_ci	inode_unlock(inode);
27562306a36Sopenharmony_ci	if (status)
27662306a36Sopenharmony_ci		trace_ext2_dio_write_buff_end(iocb, from, status);
27762306a36Sopenharmony_ci	trace_ext2_dio_write_end(iocb, from, ret);
27862306a36Sopenharmony_ci	return ret;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic ssize_t ext2_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci#ifdef CONFIG_FS_DAX
28462306a36Sopenharmony_ci	if (IS_DAX(iocb->ki_filp->f_mapping->host))
28562306a36Sopenharmony_ci		return ext2_dax_read_iter(iocb, to);
28662306a36Sopenharmony_ci#endif
28762306a36Sopenharmony_ci	if (iocb->ki_flags & IOCB_DIRECT)
28862306a36Sopenharmony_ci		return ext2_dio_read_iter(iocb, to);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return generic_file_read_iter(iocb, to);
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic ssize_t ext2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci#ifdef CONFIG_FS_DAX
29662306a36Sopenharmony_ci	if (IS_DAX(iocb->ki_filp->f_mapping->host))
29762306a36Sopenharmony_ci		return ext2_dax_write_iter(iocb, from);
29862306a36Sopenharmony_ci#endif
29962306a36Sopenharmony_ci	if (iocb->ki_flags & IOCB_DIRECT)
30062306a36Sopenharmony_ci		return ext2_dio_write_iter(iocb, from);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return generic_file_write_iter(iocb, from);
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ciconst struct file_operations ext2_file_operations = {
30662306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
30762306a36Sopenharmony_ci	.read_iter	= ext2_file_read_iter,
30862306a36Sopenharmony_ci	.write_iter	= ext2_file_write_iter,
30962306a36Sopenharmony_ci	.unlocked_ioctl = ext2_ioctl,
31062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
31162306a36Sopenharmony_ci	.compat_ioctl	= ext2_compat_ioctl,
31262306a36Sopenharmony_ci#endif
31362306a36Sopenharmony_ci	.mmap		= ext2_file_mmap,
31462306a36Sopenharmony_ci	.open		= dquot_file_open,
31562306a36Sopenharmony_ci	.release	= ext2_release_file,
31662306a36Sopenharmony_ci	.fsync		= ext2_fsync,
31762306a36Sopenharmony_ci	.get_unmapped_area = thp_get_unmapped_area,
31862306a36Sopenharmony_ci	.splice_read	= filemap_splice_read,
31962306a36Sopenharmony_ci	.splice_write	= iter_file_splice_write,
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ciconst struct inode_operations ext2_file_inode_operations = {
32362306a36Sopenharmony_ci	.listxattr	= ext2_listxattr,
32462306a36Sopenharmony_ci	.getattr	= ext2_getattr,
32562306a36Sopenharmony_ci	.setattr	= ext2_setattr,
32662306a36Sopenharmony_ci	.get_inode_acl	= ext2_get_acl,
32762306a36Sopenharmony_ci	.set_acl	= ext2_set_acl,
32862306a36Sopenharmony_ci	.fiemap		= ext2_fiemap,
32962306a36Sopenharmony_ci	.fileattr_get	= ext2_fileattr_get,
33062306a36Sopenharmony_ci	.fileattr_set	= ext2_fileattr_set,
33162306a36Sopenharmony_ci};
332