2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/quota.h>
2008-07-25 12:46:51 +04:00
# include <linux/quotaops.h>
2005-04-17 02:20:36 +04:00
# include <linux/dqblk_v1.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <asm/byteorder.h>
2008-09-22 01:17:53 +04:00
# include "quotaio_v1.h"
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Jan Kara " ) ;
MODULE_DESCRIPTION ( " Old quota format support " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-08-20 16:45:12 +04:00
# define QUOTABLOCK_BITS 10
# define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
static inline qsize_t v1_stoqb ( qsize_t space )
{
return ( space + QUOTABLOCK_SIZE - 1 ) > > QUOTABLOCK_BITS ;
}
static inline qsize_t v1_qbtos ( qsize_t blocks )
{
return blocks < < QUOTABLOCK_BITS ;
}
2005-04-17 02:20:36 +04:00
static void v1_disk2mem_dqblk ( struct mem_dqblk * m , struct v1_disk_dqblk * d )
{
m - > dqb_ihardlimit = d - > dqb_ihardlimit ;
m - > dqb_isoftlimit = d - > dqb_isoftlimit ;
m - > dqb_curinodes = d - > dqb_curinodes ;
2008-08-20 16:45:12 +04:00
m - > dqb_bhardlimit = v1_qbtos ( d - > dqb_bhardlimit ) ;
m - > dqb_bsoftlimit = v1_qbtos ( d - > dqb_bsoftlimit ) ;
m - > dqb_curspace = v1_qbtos ( d - > dqb_curblocks ) ;
2005-04-17 02:20:36 +04:00
m - > dqb_itime = d - > dqb_itime ;
m - > dqb_btime = d - > dqb_btime ;
}
static void v1_mem2disk_dqblk ( struct v1_disk_dqblk * d , struct mem_dqblk * m )
{
d - > dqb_ihardlimit = m - > dqb_ihardlimit ;
d - > dqb_isoftlimit = m - > dqb_isoftlimit ;
d - > dqb_curinodes = m - > dqb_curinodes ;
2008-08-20 16:45:12 +04:00
d - > dqb_bhardlimit = v1_stoqb ( m - > dqb_bhardlimit ) ;
d - > dqb_bsoftlimit = v1_stoqb ( m - > dqb_bsoftlimit ) ;
d - > dqb_curblocks = v1_stoqb ( m - > dqb_curspace ) ;
2005-04-17 02:20:36 +04:00
d - > dqb_itime = m - > dqb_itime ;
d - > dqb_btime = m - > dqb_btime ;
}
static int v1_read_dqblk ( struct dquot * dquot )
{
2012-09-16 14:56:19 +04:00
int type = dquot - > dq_id . type ;
2005-04-17 02:20:36 +04:00
struct v1_disk_dqblk dqblk ;
2017-06-08 16:30:45 +03:00
struct quota_info * dqopt = sb_dqopt ( dquot - > dq_sb ) ;
2005-04-17 02:20:36 +04:00
2017-06-08 16:30:45 +03:00
if ( ! dqopt - > files [ type ] )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
/* Set structure to 0s in case read fails/is after end of file */
memset ( & dqblk , 0 , sizeof ( struct v1_disk_dqblk ) ) ;
2009-01-27 17:47:22 +03:00
dquot - > dq_sb - > s_op - > quota_read ( dquot - > dq_sb , type , ( char * ) & dqblk ,
2012-09-16 14:56:19 +04:00
sizeof ( struct v1_disk_dqblk ) ,
v1_dqoff ( from_kqid ( & init_user_ns , dquot - > dq_id ) ) ) ;
2005-04-17 02:20:36 +04:00
v1_disk2mem_dqblk ( & dquot - > dq_dqb , & dqblk ) ;
2009-01-27 17:47:22 +03:00
if ( dquot - > dq_dqb . dqb_bhardlimit = = 0 & &
dquot - > dq_dqb . dqb_bsoftlimit = = 0 & &
dquot - > dq_dqb . dqb_ihardlimit = = 0 & &
dquot - > dq_dqb . dqb_isoftlimit = = 0 )
2005-04-17 02:20:36 +04:00
set_bit ( DQ_FAKE_B , & dquot - > dq_flags ) ;
2010-04-26 20:03:33 +04:00
dqstats_inc ( DQST_READS ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int v1_commit_dqblk ( struct dquot * dquot )
{
2012-09-16 14:56:19 +04:00
short type = dquot - > dq_id . type ;
2005-04-17 02:20:36 +04:00
ssize_t ret ;
struct v1_disk_dqblk dqblk ;
v1_mem2disk_dqblk ( & dqblk , & dquot - > dq_dqb ) ;
2012-09-16 14:56:19 +04:00
if ( ( ( type = = USRQUOTA ) & & uid_eq ( dquot - > dq_id . uid , GLOBAL_ROOT_UID ) ) | |
( ( type = = GRPQUOTA ) & & gid_eq ( dquot - > dq_id . gid , GLOBAL_ROOT_GID ) ) ) {
2009-01-27 17:47:22 +03:00
dqblk . dqb_btime =
sb_dqopt ( dquot - > dq_sb ) - > info [ type ] . dqi_bgrace ;
dqblk . dqb_itime =
sb_dqopt ( dquot - > dq_sb ) - > info [ type ] . dqi_igrace ;
2005-04-17 02:20:36 +04:00
}
ret = 0 ;
if ( sb_dqopt ( dquot - > dq_sb ) - > files [ type ] )
2009-01-27 17:47:22 +03:00
ret = dquot - > dq_sb - > s_op - > quota_write ( dquot - > dq_sb , type ,
( char * ) & dqblk , sizeof ( struct v1_disk_dqblk ) ,
2012-09-16 14:56:19 +04:00
v1_dqoff ( from_kqid ( & init_user_ns , dquot - > dq_id ) ) ) ;
2005-04-17 02:20:36 +04:00
if ( ret ! = sizeof ( struct v1_disk_dqblk ) ) {
2010-07-20 18:54:43 +04:00
quota_error ( dquot - > dq_sb , " dquota write failed " ) ;
2005-04-17 02:20:36 +04:00
if ( ret > = 0 )
ret = - EIO ;
goto out ;
}
ret = 0 ;
out :
2010-04-26 20:03:33 +04:00
dqstats_inc ( DQST_WRITES ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
/* Magics of new quota format */
# define V2_INITQMAGICS {\
0xd9c01f11 , /* USRQUOTA */ \
0xd9c01927 /* GRPQUOTA */ \
}
/* Header of new quota format */
struct v2_disk_dqheader {
__le32 dqh_magic ; /* Magic number identifying file */
__le32 dqh_version ; /* File version */
} ;
static int v1_check_quota_file ( struct super_block * sb , int type )
{
struct inode * inode = sb_dqopt ( sb ) - > files [ type ] ;
ulong blocks ;
2019-03-26 10:54:31 +03:00
size_t off ;
2005-04-17 02:20:36 +04:00
struct v2_disk_dqheader dqhead ;
ssize_t size ;
loff_t isize ;
static const uint quota_magics [ ] = V2_INITQMAGICS ;
isize = i_size_read ( inode ) ;
if ( ! isize )
return 0 ;
blocks = isize > > BLOCK_SIZE_BITS ;
off = isize & ( BLOCK_SIZE - 1 ) ;
2009-01-27 17:47:22 +03:00
if ( ( blocks % sizeof ( struct v1_disk_dqblk ) * BLOCK_SIZE + off ) %
sizeof ( struct v1_disk_dqblk ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2009-01-27 17:47:22 +03:00
/* Doublecheck whether we didn't get file with new format - with old
* quotactl ( ) this could happen */
size = sb - > s_op - > quota_read ( sb , type , ( char * ) & dqhead ,
sizeof ( struct v2_disk_dqheader ) , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( size ! = sizeof ( struct v2_disk_dqheader ) )
return 1 ; /* Probably not new format */
if ( le32_to_cpu ( dqhead . dqh_magic ) ! = quota_magics [ type ] )
return 1 ; /* Definitely not new format */
2009-01-27 17:47:22 +03:00
printk ( KERN_INFO
" VFS: %s: Refusing to turn on old quota format on given file. "
" It probably contains newer quota format. \n " , sb - > s_id ) ;
2005-04-17 02:20:36 +04:00
return 0 ; /* Seems like a new format file -> refuse it */
}
static int v1_read_file_info ( struct super_block * sb , int type )
{
struct quota_info * dqopt = sb_dqopt ( sb ) ;
struct v1_disk_dqblk dqblk ;
int ret ;
2017-06-09 09:59:46 +03:00
down_read ( & dqopt - > dqio_sem ) ;
2009-01-27 17:47:22 +03:00
ret = sb - > s_op - > quota_read ( sb , type , ( char * ) & dqblk ,
sizeof ( struct v1_disk_dqblk ) , v1_dqoff ( 0 ) ) ;
if ( ret ! = sizeof ( struct v1_disk_dqblk ) ) {
2005-04-17 02:20:36 +04:00
if ( ret > = 0 )
ret = - EIO ;
goto out ;
}
ret = 0 ;
2008-04-28 13:14:31 +04:00
/* limits are stored as unsigned 32-bit data */
2014-10-09 18:54:13 +04:00
dqopt - > info [ type ] . dqi_max_spc_limit = 0xffffffffULL < < QUOTABLOCK_BITS ;
dqopt - > info [ type ] . dqi_max_ino_limit = 0xffffffff ;
2009-01-27 17:47:22 +03:00
dqopt - > info [ type ] . dqi_igrace =
dqblk . dqb_itime ? dqblk . dqb_itime : MAX_IQ_TIME ;
dqopt - > info [ type ] . dqi_bgrace =
dqblk . dqb_btime ? dqblk . dqb_btime : MAX_DQ_TIME ;
2005-04-17 02:20:36 +04:00
out :
2017-06-09 09:59:46 +03:00
up_read ( & dqopt - > dqio_sem ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static int v1_write_file_info ( struct super_block * sb , int type )
{
struct quota_info * dqopt = sb_dqopt ( sb ) ;
struct v1_disk_dqblk dqblk ;
int ret ;
2017-06-09 09:45:43 +03:00
down_write ( & dqopt - > dqio_sem ) ;
2009-01-27 17:47:22 +03:00
ret = sb - > s_op - > quota_read ( sb , type , ( char * ) & dqblk ,
sizeof ( struct v1_disk_dqblk ) , v1_dqoff ( 0 ) ) ;
if ( ret ! = sizeof ( struct v1_disk_dqblk ) ) {
2005-04-17 02:20:36 +04:00
if ( ret > = 0 )
ret = - EIO ;
goto out ;
}
2017-06-09 12:56:06 +03:00
spin_lock ( & dq_data_lock ) ;
dqopt - > info [ type ] . dqi_flags & = ~ DQF_INFO_DIRTY ;
2005-04-17 02:20:36 +04:00
dqblk . dqb_itime = dqopt - > info [ type ] . dqi_igrace ;
dqblk . dqb_btime = dqopt - > info [ type ] . dqi_bgrace ;
2017-06-09 12:56:06 +03:00
spin_unlock ( & dq_data_lock ) ;
2005-04-17 02:20:36 +04:00
ret = sb - > s_op - > quota_write ( sb , type , ( char * ) & dqblk ,
sizeof ( struct v1_disk_dqblk ) , v1_dqoff ( 0 ) ) ;
if ( ret = = sizeof ( struct v1_disk_dqblk ) )
ret = 0 ;
else if ( ret > 0 )
ret = - EIO ;
out :
2017-06-09 09:45:43 +03:00
up_write ( & dqopt - > dqio_sem ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2009-10-16 15:26:03 +04:00
static const struct quota_format_ops v1_format_ops = {
2005-04-17 02:20:36 +04:00
. check_quota_file = v1_check_quota_file ,
. read_file_info = v1_read_file_info ,
. write_file_info = v1_write_file_info ,
. read_dqblk = v1_read_dqblk ,
. commit_dqblk = v1_commit_dqblk ,
} ;
static struct quota_format_type v1_quota_format = {
. qf_fmt_id = QFMT_VFS_OLD ,
. qf_ops = & v1_format_ops ,
. qf_owner = THIS_MODULE
} ;
static int __init init_v1_quota_format ( void )
{
return register_quota_format ( & v1_quota_format ) ;
}
static void __exit exit_v1_quota_format ( void )
{
unregister_quota_format ( & v1_quota_format ) ;
}
module_init ( init_v1_quota_format ) ;
module_exit ( exit_v1_quota_format ) ;