2005-04-16 15:20:36 -07:00
/*
* file . c
*
* PURPOSE
* File handling routines for the OSTA - UDF ( tm ) filesystem .
*
* COPYRIGHT
* This file is distributed under the terms of the GNU General Public
* License ( GPL ) . Copies of the GPL can be obtained from :
* ftp : //prep.ai.mit.edu/pub/gnu/GPL
* Each contributing author retains all rights to their own work .
*
* ( C ) 1998 - 1999 Dave Boynton
* ( C ) 1998 - 2004 Ben Fennema
* ( C ) 1999 - 2000 Stelias Computing Inc
*
* HISTORY
*
* 10 / 02 / 98 dgb Attempt to integrate into udf . o
* 10 / 07 / 98 Switched to using generic_readpage , etc . , like isofs
* And it works !
* 12 / 06 / 98 blf Added udf_file_read . uses generic_file_read for all cases but
* ICBTAG_FLAG_AD_IN_ICB .
* 04 / 06 / 99 64 bit file handling on 32 bit systems taken from ext2 file . c
* 05 / 12 / 99 Preliminary file write support
*/
# include "udfdecl.h"
# include <linux/fs.h>
2014-06-18 19:38:24 +02:00
# include <linux/uaccess.h>
2005-04-16 15:20:36 -07:00
# include <linux/kernel.h>
2007-07-21 04:37:18 -07:00
# include <linux/string.h> /* memset */
2006-01-11 12:17:46 -08:00
# include <linux/capability.h>
2005-04-16 15:20:36 -07:00
# include <linux/errno.h>
# include <linux/pagemap.h>
2015-02-22 08:58:50 -08:00
# include <linux/uio.h>
2005-04-16 15:20:36 -07:00
# include "udf_i.h"
# include "udf_sb.h"
2023-01-19 10:51:21 +01:00
static vm_fault_t udf_page_mkwrite ( struct vm_fault * vmf )
2005-04-16 15:20:36 -07:00
{
2023-01-19 10:51:21 +01:00
struct vm_area_struct * vma = vmf - > vma ;
struct inode * inode = file_inode ( vma - > vm_file ) ;
struct address_space * mapping = inode - > i_mapping ;
struct page * page = vmf - > page ;
loff_t size ;
unsigned int end ;
vm_fault_t ret = VM_FAULT_LOCKED ;
int err ;
2012-09-05 17:44:31 +01:00
2023-01-19 10:51:21 +01:00
sb_start_pagefault ( inode - > i_sb ) ;
file_update_time ( vma - > vm_file ) ;
filemap_invalidate_lock_shared ( mapping ) ;
lock_page ( page ) ;
size = i_size_read ( inode ) ;
if ( page - > mapping ! = inode - > i_mapping | | page_offset ( page ) > = size ) {
unlock_page ( page ) ;
ret = VM_FAULT_NOPAGE ;
goto out_unlock ;
}
/* Space is already allocated for in-ICB file */
if ( UDF_I ( inode ) - > i_alloc_type = = ICBTAG_FLAG_AD_IN_ICB )
goto out_dirty ;
if ( page - > index = = size > > PAGE_SHIFT )
end = size & ~ PAGE_MASK ;
else
end = PAGE_SIZE ;
err = __block_write_begin ( page , 0 , end , udf_get_block ) ;
if ( ! err )
err = block_commit_write ( page , 0 , end ) ;
if ( err < 0 ) {
unlock_page ( page ) ;
ret = block_page_mkwrite_return ( err ) ;
goto out_unlock ;
}
out_dirty :
2016-09-16 00:17:21 +01:00
set_page_dirty ( page ) ;
2023-01-19 10:51:21 +01:00
wait_for_stable_page ( page ) ;
out_unlock :
filemap_invalidate_unlock_shared ( mapping ) ;
sb_end_pagefault ( inode - > i_sb ) ;
return ret ;
2016-09-16 00:17:21 +01:00
}
2023-01-19 10:51:21 +01:00
static const struct vm_operations_struct udf_file_vm_ops = {
. fault = filemap_fault ,
. map_pages = filemap_map_pages ,
. page_mkwrite = udf_page_mkwrite ,
2005-04-16 15:20:36 -07:00
} ;
2014-04-03 03:31:17 -04:00
static ssize_t udf_file_write_iter ( struct kiocb * iocb , struct iov_iter * from )
2005-04-16 15:20:36 -07:00
{
ssize_t retval ;
2006-09-30 23:28:48 -07:00
struct file * file = iocb - > ki_filp ;
2013-01-23 17:07:38 -05:00
struct inode * inode = file_inode ( file ) ;
2008-02-08 04:20:44 -08:00
struct udf_inode_info * iinfo = UDF_I ( inode ) ;
2005-04-16 15:20:36 -07:00
2016-01-22 15:40:57 -05:00
inode_lock ( inode ) ;
2015-04-07 15:26:36 -04:00
2015-04-09 12:55:47 -04:00
retval = generic_write_checks ( iocb , from ) ;
if ( retval < = 0 )
2015-04-07 15:26:36 -04:00
goto out ;
2023-01-02 20:14:47 +01:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_IN_ICB & &
inode - > i_sb - > s_blocksize < ( udf_file_entry_alloc_offset ( inode ) +
iocb - > ki_pos + iov_iter_count ( from ) ) ) {
2023-01-19 12:46:09 +01:00
filemap_invalidate_lock ( inode - > i_mapping ) ;
2023-01-19 12:44:34 +01:00
retval = udf_expand_file_adinicb ( inode ) ;
2023-01-19 12:46:09 +01:00
filemap_invalidate_unlock ( inode - > i_mapping ) ;
2023-01-19 12:44:34 +01:00
if ( retval )
goto out ;
2023-01-19 12:28:37 +01:00
}
2005-04-16 15:20:36 -07:00
2014-04-03 03:31:17 -04:00
retval = __generic_file_write_iter ( iocb , from ) ;
2015-04-07 11:28:12 -04:00
out :
2023-01-19 12:28:37 +01:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_IN_ICB & & retval > 0 ) {
down_write ( & iinfo - > i_data_sem ) ;
2023-01-02 20:14:47 +01:00
iinfo - > i_lenAlloc = inode - > i_size ;
2023-01-19 12:28:37 +01:00
up_write ( & iinfo - > i_data_sem ) ;
}
2016-01-22 15:40:57 -05:00
inode_unlock ( inode ) ;
2014-02-18 12:00:21 +01:00
if ( retval > 0 ) {
2005-04-16 15:20:36 -07:00
mark_inode_dirty ( inode ) ;
2016-04-07 08:52:01 -07:00
retval = generic_write_sync ( iocb , retval ) ;
2014-02-18 12:00:21 +01:00
}
2007-07-21 04:37:18 -07:00
2005-04-16 15:20:36 -07:00
return retval ;
}
2010-05-05 15:15:39 +02:00
long udf_ioctl ( struct file * filp , unsigned int cmd , unsigned long arg )
2005-04-16 15:20:36 -07:00
{
2013-01-23 17:07:38 -05:00
struct inode * inode = file_inode ( filp ) ;
2007-07-21 04:37:18 -07:00
long old_block , new_block ;
2017-01-24 21:48:35 +01:00
int result ;
2005-04-16 15:20:36 -07:00
2021-01-21 14:19:22 +01:00
if ( file_permission ( filp , MAY_READ ) ! = 0 ) {
2010-05-05 15:15:39 +02:00
udf_debug ( " no permission to access inode %lu \n " , inode - > i_ino ) ;
2017-01-24 21:48:35 +01:00
return - EPERM ;
2005-04-16 15:20:36 -07:00
}
2017-01-24 21:48:34 +01:00
if ( ! arg & & ( ( cmd = = UDF_GETVOLIDENT ) | | ( cmd = = UDF_GETEASIZE ) | |
( cmd = = UDF_RELOCATE_BLOCKS ) | | ( cmd = = UDF_GETEABLOCK ) ) ) {
2005-04-16 15:20:36 -07:00
udf_debug ( " invalid argument to udf_ioctl \n " ) ;
2017-01-24 21:48:35 +01:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
}
2007-07-19 01:47:43 -07:00
switch ( cmd ) {
case UDF_GETVOLIDENT :
2008-02-08 04:20:36 -08:00
if ( copy_to_user ( ( char __user * ) arg ,
UDF_SB ( inode - > i_sb ) - > s_volume_ident , 32 ) )
2017-01-24 21:48:35 +01:00
return - EFAULT ;
return 0 ;
2007-07-19 01:47:43 -07:00
case UDF_RELOCATE_BLOCKS :
2017-01-24 21:48:35 +01:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( get_user ( old_block , ( long __user * ) arg ) )
return - EFAULT ;
2008-02-08 04:20:36 -08:00
result = udf_relocate_blocks ( inode - > i_sb ,
old_block , & new_block ) ;
if ( result = = 0 )
2007-07-21 04:37:18 -07:00
result = put_user ( new_block , ( long __user * ) arg ) ;
2017-01-24 21:48:35 +01:00
return result ;
2007-07-19 01:47:43 -07:00
case UDF_GETEASIZE :
2017-01-24 21:48:35 +01:00
return put_user ( UDF_I ( inode ) - > i_lenEAttr , ( int __user * ) arg ) ;
2007-07-19 01:47:43 -07:00
case UDF_GETEABLOCK :
2017-01-24 21:48:35 +01:00
return copy_to_user ( ( char __user * ) arg ,
2020-09-25 12:29:54 +02:00
UDF_I ( inode ) - > i_data ,
2017-01-24 21:48:35 +01:00
UDF_I ( inode ) - > i_lenEAttr ) ? - EFAULT : 0 ;
2017-01-24 21:48:34 +01:00
default :
return - ENOIOCTLCMD ;
2005-04-16 15:20:36 -07:00
}
2017-01-24 21:48:35 +01:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-07-19 01:47:43 -07:00
static int udf_release_file ( struct inode * inode , struct file * filp )
2005-04-16 15:20:36 -07:00
{
2014-09-09 13:03:03 +02:00
if ( filp - > f_mode & FMODE_WRITE & &
2015-01-28 08:38:20 +01:00
atomic_read ( & inode - > i_writecount ) = = 1 ) {
2014-09-09 13:03:03 +02:00
/*
* Grab i_mutex to avoid races with writes changing i_size
* while we are running .
*/
2016-01-22 15:40:57 -05:00
inode_lock ( inode ) ;
2010-11-16 18:40:47 +01:00
down_write ( & UDF_I ( inode ) - > i_data_sem ) ;
2005-04-16 15:20:36 -07:00
udf_discard_prealloc ( inode ) ;
2009-12-03 13:39:28 +01:00
udf_truncate_tail_extent ( inode ) ;
2010-11-16 18:40:47 +01:00
up_write ( & UDF_I ( inode ) - > i_data_sem ) ;
2016-01-22 15:40:57 -05:00
inode_unlock ( inode ) ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
}
2023-01-19 10:51:21 +01:00
static int udf_file_mmap ( struct file * file , struct vm_area_struct * vma )
{
file_accessed ( file ) ;
vma - > vm_ops = & udf_file_vm_ops ;
return 0 ;
}
2006-03-28 01:56:42 -08:00
const struct file_operations udf_file_operations = {
2014-04-02 14:33:16 -04:00
. read_iter = generic_file_read_iter ,
2010-05-05 15:15:39 +02:00
. unlocked_ioctl = udf_ioctl ,
2010-05-19 16:28:56 +02:00
. open = generic_file_open ,
2023-01-19 10:51:21 +01:00
. mmap = udf_file_mmap ,
2014-04-03 03:31:17 -04:00
. write_iter = udf_file_write_iter ,
2007-07-21 04:37:18 -07:00
. release = udf_release_file ,
2010-05-26 17:53:41 +02:00
. fsync = generic_file_fsync ,
2007-07-21 04:37:18 -07:00
. splice_read = generic_file_splice_read ,
2022-09-12 12:25:29 +02:00
. splice_write = iter_file_splice_write ,
2008-09-08 19:44:17 +02:00
. llseek = generic_file_llseek ,
2005-04-16 15:20:36 -07:00
} ;
2023-01-13 12:49:11 +01:00
static int udf_setattr ( struct mnt_idmap * idmap , struct dentry * dentry ,
2021-01-21 14:19:43 +01:00
struct iattr * attr )
2010-06-04 11:29:59 +02:00
{
2015-03-17 22:25:59 +00:00
struct inode * inode = d_inode ( dentry ) ;
2018-02-21 17:59:31 +01:00
struct super_block * sb = inode - > i_sb ;
2010-06-04 11:29:59 +02:00
int error ;
2023-01-13 12:49:11 +01:00
error = setattr_prepare ( & nop_mnt_idmap , dentry , attr ) ;
2010-06-04 11:29:59 +02:00
if ( error )
return error ;
2010-06-04 11:30:02 +02:00
2018-02-21 17:59:31 +01:00
if ( ( attr - > ia_valid & ATTR_UID ) & &
UDF_QUERY_FLAG ( sb , UDF_FLAG_UID_SET ) & &
! uid_eq ( attr - > ia_uid , UDF_SB ( sb ) - > s_uid ) )
return - EPERM ;
if ( ( attr - > ia_valid & ATTR_GID ) & &
UDF_QUERY_FLAG ( sb , UDF_FLAG_GID_SET ) & &
! gid_eq ( attr - > ia_gid , UDF_SB ( sb ) - > s_gid ) )
return - EPERM ;
2010-06-04 11:30:02 +02:00
if ( ( attr - > ia_valid & ATTR_SIZE ) & &
attr - > ia_size ! = i_size_read ( inode ) ) {
2010-10-22 00:30:26 +02:00
error = udf_setsize ( inode , attr - > ia_size ) ;
2010-06-04 11:30:02 +02:00
if ( error )
return error ;
}
2019-08-27 07:13:59 -05:00
if ( attr - > ia_valid & ATTR_MODE )
udf_update_extra_perms ( inode , attr - > ia_mode ) ;
2023-01-13 12:49:11 +01:00
setattr_copy ( & nop_mnt_idmap , inode , attr ) ;
2010-06-04 11:30:02 +02:00
mark_inode_dirty ( inode ) ;
return 0 ;
2010-06-04 11:29:59 +02:00
}
2007-02-12 00:55:40 -08:00
const struct inode_operations udf_file_inode_operations = {
2010-06-04 11:29:59 +02:00
. setattr = udf_setattr ,
2005-04-16 15:20:36 -07:00
} ;