18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * file.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * PURPOSE
58c2ecf20Sopenharmony_ci *  File handling routines for the OSTA-UDF(tm) filesystem.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * COPYRIGHT
88c2ecf20Sopenharmony_ci *  This file is distributed under the terms of the GNU General Public
98c2ecf20Sopenharmony_ci *  License (GPL). Copies of the GPL can be obtained from:
108c2ecf20Sopenharmony_ci *    ftp://prep.ai.mit.edu/pub/gnu/GPL
118c2ecf20Sopenharmony_ci *  Each contributing author retains all rights to their own work.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *  (C) 1998-1999 Dave Boynton
148c2ecf20Sopenharmony_ci *  (C) 1998-2004 Ben Fennema
158c2ecf20Sopenharmony_ci *  (C) 1999-2000 Stelias Computing Inc
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * HISTORY
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *  10/02/98 dgb  Attempt to integrate into udf.o
208c2ecf20Sopenharmony_ci *  10/07/98      Switched to using generic_readpage, etc., like isofs
218c2ecf20Sopenharmony_ci *                And it works!
228c2ecf20Sopenharmony_ci *  12/06/98 blf  Added udf_file_read. uses generic_file_read for all cases but
238c2ecf20Sopenharmony_ci *                ICBTAG_FLAG_AD_IN_ICB.
248c2ecf20Sopenharmony_ci *  04/06/99      64 bit file handling on 32 bit systems taken from ext2 file.c
258c2ecf20Sopenharmony_ci *  05/12/99      Preliminary file write support
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "udfdecl.h"
298c2ecf20Sopenharmony_ci#include <linux/fs.h>
308c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
318c2ecf20Sopenharmony_ci#include <linux/kernel.h>
328c2ecf20Sopenharmony_ci#include <linux/string.h> /* memset */
338c2ecf20Sopenharmony_ci#include <linux/capability.h>
348c2ecf20Sopenharmony_ci#include <linux/errno.h>
358c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
368c2ecf20Sopenharmony_ci#include <linux/uio.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#include "udf_i.h"
398c2ecf20Sopenharmony_ci#include "udf_sb.h"
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void __udf_adinicb_readpage(struct page *page)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct inode *inode = page->mapping->host;
448c2ecf20Sopenharmony_ci	char *kaddr;
458c2ecf20Sopenharmony_ci	struct udf_inode_info *iinfo = UDF_I(inode);
468c2ecf20Sopenharmony_ci	loff_t isize = i_size_read(inode);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	/*
498c2ecf20Sopenharmony_ci	 * We have to be careful here as truncate can change i_size under us.
508c2ecf20Sopenharmony_ci	 * So just sample it once and use the same value everywhere.
518c2ecf20Sopenharmony_ci	 */
528c2ecf20Sopenharmony_ci	kaddr = kmap_atomic(page);
538c2ecf20Sopenharmony_ci	memcpy(kaddr, iinfo->i_data + iinfo->i_lenEAttr, isize);
548c2ecf20Sopenharmony_ci	memset(kaddr + isize, 0, PAGE_SIZE - isize);
558c2ecf20Sopenharmony_ci	flush_dcache_page(page);
568c2ecf20Sopenharmony_ci	SetPageUptodate(page);
578c2ecf20Sopenharmony_ci	kunmap_atomic(kaddr);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int udf_adinicb_readpage(struct file *file, struct page *page)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	BUG_ON(!PageLocked(page));
638c2ecf20Sopenharmony_ci	__udf_adinicb_readpage(page);
648c2ecf20Sopenharmony_ci	unlock_page(page);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return 0;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int udf_adinicb_writepage(struct page *page,
708c2ecf20Sopenharmony_ci				 struct writeback_control *wbc)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct inode *inode = page->mapping->host;
738c2ecf20Sopenharmony_ci	char *kaddr;
748c2ecf20Sopenharmony_ci	struct udf_inode_info *iinfo = UDF_I(inode);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	BUG_ON(!PageLocked(page));
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	kaddr = kmap_atomic(page);
798c2ecf20Sopenharmony_ci	memcpy(iinfo->i_data + iinfo->i_lenEAttr, kaddr, i_size_read(inode));
808c2ecf20Sopenharmony_ci	SetPageUptodate(page);
818c2ecf20Sopenharmony_ci	kunmap_atomic(kaddr);
828c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
838c2ecf20Sopenharmony_ci	unlock_page(page);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return 0;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int udf_adinicb_write_begin(struct file *file,
898c2ecf20Sopenharmony_ci			struct address_space *mapping, loff_t pos,
908c2ecf20Sopenharmony_ci			unsigned len, unsigned flags, struct page **pagep,
918c2ecf20Sopenharmony_ci			void **fsdata)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct page *page;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(pos >= PAGE_SIZE))
968c2ecf20Sopenharmony_ci		return -EIO;
978c2ecf20Sopenharmony_ci	page = grab_cache_page_write_begin(mapping, 0, flags);
988c2ecf20Sopenharmony_ci	if (!page)
998c2ecf20Sopenharmony_ci		return -ENOMEM;
1008c2ecf20Sopenharmony_ci	*pagep = page;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (!PageUptodate(page))
1038c2ecf20Sopenharmony_ci		__udf_adinicb_readpage(page);
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic ssize_t udf_adinicb_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	/* Fallback to buffered I/O. */
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int udf_adinicb_write_end(struct file *file, struct address_space *mapping,
1148c2ecf20Sopenharmony_ci				 loff_t pos, unsigned len, unsigned copied,
1158c2ecf20Sopenharmony_ci				 struct page *page, void *fsdata)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct inode *inode = page->mapping->host;
1188c2ecf20Sopenharmony_ci	loff_t last_pos = pos + copied;
1198c2ecf20Sopenharmony_ci	if (last_pos > inode->i_size)
1208c2ecf20Sopenharmony_ci		i_size_write(inode, last_pos);
1218c2ecf20Sopenharmony_ci	set_page_dirty(page);
1228c2ecf20Sopenharmony_ci	unlock_page(page);
1238c2ecf20Sopenharmony_ci	put_page(page);
1248c2ecf20Sopenharmony_ci	return copied;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ciconst struct address_space_operations udf_adinicb_aops = {
1288c2ecf20Sopenharmony_ci	.readpage	= udf_adinicb_readpage,
1298c2ecf20Sopenharmony_ci	.writepage	= udf_adinicb_writepage,
1308c2ecf20Sopenharmony_ci	.write_begin	= udf_adinicb_write_begin,
1318c2ecf20Sopenharmony_ci	.write_end	= udf_adinicb_write_end,
1328c2ecf20Sopenharmony_ci	.direct_IO	= udf_adinicb_direct_IO,
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	ssize_t retval;
1388c2ecf20Sopenharmony_ci	struct file *file = iocb->ki_filp;
1398c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
1408c2ecf20Sopenharmony_ci	struct udf_inode_info *iinfo = UDF_I(inode);
1418c2ecf20Sopenharmony_ci	int err;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	inode_lock(inode);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	retval = generic_write_checks(iocb, from);
1468c2ecf20Sopenharmony_ci	if (retval <= 0)
1478c2ecf20Sopenharmony_ci		goto out;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	down_write(&iinfo->i_data_sem);
1508c2ecf20Sopenharmony_ci	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB &&
1518c2ecf20Sopenharmony_ci	    inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) +
1528c2ecf20Sopenharmony_ci				 iocb->ki_pos + iov_iter_count(from))) {
1538c2ecf20Sopenharmony_ci		err = udf_expand_file_adinicb(inode);
1548c2ecf20Sopenharmony_ci		if (err) {
1558c2ecf20Sopenharmony_ci			inode_unlock(inode);
1568c2ecf20Sopenharmony_ci			udf_debug("udf_expand_adinicb: err=%d\n", err);
1578c2ecf20Sopenharmony_ci			return err;
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci	} else
1608c2ecf20Sopenharmony_ci		up_write(&iinfo->i_data_sem);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	retval = __generic_file_write_iter(iocb, from);
1638c2ecf20Sopenharmony_ciout:
1648c2ecf20Sopenharmony_ci	down_write(&iinfo->i_data_sem);
1658c2ecf20Sopenharmony_ci	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB && retval > 0)
1668c2ecf20Sopenharmony_ci		iinfo->i_lenAlloc = inode->i_size;
1678c2ecf20Sopenharmony_ci	up_write(&iinfo->i_data_sem);
1688c2ecf20Sopenharmony_ci	inode_unlock(inode);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (retval > 0) {
1718c2ecf20Sopenharmony_ci		mark_inode_dirty(inode);
1728c2ecf20Sopenharmony_ci		retval = generic_write_sync(iocb, retval);
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	return retval;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cilong udf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(filp);
1818c2ecf20Sopenharmony_ci	long old_block, new_block;
1828c2ecf20Sopenharmony_ci	int result;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (inode_permission(inode, MAY_READ) != 0) {
1858c2ecf20Sopenharmony_ci		udf_debug("no permission to access inode %lu\n", inode->i_ino);
1868c2ecf20Sopenharmony_ci		return -EPERM;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (!arg && ((cmd == UDF_GETVOLIDENT) || (cmd == UDF_GETEASIZE) ||
1908c2ecf20Sopenharmony_ci		     (cmd == UDF_RELOCATE_BLOCKS) || (cmd == UDF_GETEABLOCK))) {
1918c2ecf20Sopenharmony_ci		udf_debug("invalid argument to udf_ioctl\n");
1928c2ecf20Sopenharmony_ci		return -EINVAL;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	switch (cmd) {
1968c2ecf20Sopenharmony_ci	case UDF_GETVOLIDENT:
1978c2ecf20Sopenharmony_ci		if (copy_to_user((char __user *)arg,
1988c2ecf20Sopenharmony_ci				 UDF_SB(inode->i_sb)->s_volume_ident, 32))
1998c2ecf20Sopenharmony_ci			return -EFAULT;
2008c2ecf20Sopenharmony_ci		return 0;
2018c2ecf20Sopenharmony_ci	case UDF_RELOCATE_BLOCKS:
2028c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
2038c2ecf20Sopenharmony_ci			return -EPERM;
2048c2ecf20Sopenharmony_ci		if (get_user(old_block, (long __user *)arg))
2058c2ecf20Sopenharmony_ci			return -EFAULT;
2068c2ecf20Sopenharmony_ci		result = udf_relocate_blocks(inode->i_sb,
2078c2ecf20Sopenharmony_ci						old_block, &new_block);
2088c2ecf20Sopenharmony_ci		if (result == 0)
2098c2ecf20Sopenharmony_ci			result = put_user(new_block, (long __user *)arg);
2108c2ecf20Sopenharmony_ci		return result;
2118c2ecf20Sopenharmony_ci	case UDF_GETEASIZE:
2128c2ecf20Sopenharmony_ci		return put_user(UDF_I(inode)->i_lenEAttr, (int __user *)arg);
2138c2ecf20Sopenharmony_ci	case UDF_GETEABLOCK:
2148c2ecf20Sopenharmony_ci		return copy_to_user((char __user *)arg,
2158c2ecf20Sopenharmony_ci				    UDF_I(inode)->i_data,
2168c2ecf20Sopenharmony_ci				    UDF_I(inode)->i_lenEAttr) ? -EFAULT : 0;
2178c2ecf20Sopenharmony_ci	default:
2188c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return 0;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int udf_release_file(struct inode *inode, struct file *filp)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	if (filp->f_mode & FMODE_WRITE &&
2278c2ecf20Sopenharmony_ci	    atomic_read(&inode->i_writecount) == 1) {
2288c2ecf20Sopenharmony_ci		/*
2298c2ecf20Sopenharmony_ci		 * Grab i_mutex to avoid races with writes changing i_size
2308c2ecf20Sopenharmony_ci		 * while we are running.
2318c2ecf20Sopenharmony_ci		 */
2328c2ecf20Sopenharmony_ci		inode_lock(inode);
2338c2ecf20Sopenharmony_ci		down_write(&UDF_I(inode)->i_data_sem);
2348c2ecf20Sopenharmony_ci		udf_discard_prealloc(inode);
2358c2ecf20Sopenharmony_ci		udf_truncate_tail_extent(inode);
2368c2ecf20Sopenharmony_ci		up_write(&UDF_I(inode)->i_data_sem);
2378c2ecf20Sopenharmony_ci		inode_unlock(inode);
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ciconst struct file_operations udf_file_operations = {
2438c2ecf20Sopenharmony_ci	.read_iter		= generic_file_read_iter,
2448c2ecf20Sopenharmony_ci	.unlocked_ioctl		= udf_ioctl,
2458c2ecf20Sopenharmony_ci	.open			= generic_file_open,
2468c2ecf20Sopenharmony_ci	.mmap			= generic_file_mmap,
2478c2ecf20Sopenharmony_ci	.write_iter		= udf_file_write_iter,
2488c2ecf20Sopenharmony_ci	.release		= udf_release_file,
2498c2ecf20Sopenharmony_ci	.fsync			= generic_file_fsync,
2508c2ecf20Sopenharmony_ci	.splice_read		= generic_file_splice_read,
2518c2ecf20Sopenharmony_ci	.llseek			= generic_file_llseek,
2528c2ecf20Sopenharmony_ci};
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic int udf_setattr(struct dentry *dentry, struct iattr *attr)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(dentry);
2578c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
2588c2ecf20Sopenharmony_ci	int error;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	error = setattr_prepare(dentry, attr);
2618c2ecf20Sopenharmony_ci	if (error)
2628c2ecf20Sopenharmony_ci		return error;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if ((attr->ia_valid & ATTR_UID) &&
2658c2ecf20Sopenharmony_ci	    UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET) &&
2668c2ecf20Sopenharmony_ci	    !uid_eq(attr->ia_uid, UDF_SB(sb)->s_uid))
2678c2ecf20Sopenharmony_ci		return -EPERM;
2688c2ecf20Sopenharmony_ci	if ((attr->ia_valid & ATTR_GID) &&
2698c2ecf20Sopenharmony_ci	    UDF_QUERY_FLAG(sb, UDF_FLAG_GID_SET) &&
2708c2ecf20Sopenharmony_ci	    !gid_eq(attr->ia_gid, UDF_SB(sb)->s_gid))
2718c2ecf20Sopenharmony_ci		return -EPERM;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if ((attr->ia_valid & ATTR_SIZE) &&
2748c2ecf20Sopenharmony_ci	    attr->ia_size != i_size_read(inode)) {
2758c2ecf20Sopenharmony_ci		error = udf_setsize(inode, attr->ia_size);
2768c2ecf20Sopenharmony_ci		if (error)
2778c2ecf20Sopenharmony_ci			return error;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (attr->ia_valid & ATTR_MODE)
2818c2ecf20Sopenharmony_ci		udf_update_extra_perms(inode, attr->ia_mode);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	setattr_copy(inode, attr);
2848c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ciconst struct inode_operations udf_file_inode_operations = {
2898c2ecf20Sopenharmony_ci	.setattr		= udf_setattr,
2908c2ecf20Sopenharmony_ci};
291