2005-04-17 02:20:36 +04:00
/*
* vfsv0 quota IO operations on file
*/
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/mount.h>
# include <linux/dqblk_v2.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/slab.h>
2008-07-25 12:46:51 +04:00
# include <linux/quotaops.h>
2005-04-17 02:20:36 +04:00
# include <asm/byteorder.h>
2008-09-22 07:54:49 +04:00
# include "quota_tree.h"
2008-09-22 01:17:53 +04:00
# include "quotaio_v2.h"
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Jan Kara " ) ;
MODULE_DESCRIPTION ( " Quota format v2 support " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define __QUOTA_V2_PARANOIA
2008-09-22 07:54:49 +04:00
static void v2_mem2diskdqb ( void * dp , struct dquot * dquot ) ;
static void v2_disk2memdqb ( struct dquot * dquot , void * dp ) ;
static int v2_is_id ( void * dp , struct dquot * dquot ) ;
2005-04-17 02:20:36 +04:00
2008-09-22 07:54:49 +04:00
static struct qtree_fmt_operations v2_qtree_ops = {
. mem2disk_dqblk = v2_mem2diskdqb ,
. disk2mem_dqblk = v2_disk2memdqb ,
. is_id = v2_is_id ,
} ;
2005-04-17 02:20:36 +04:00
2008-08-20 16:45:12 +04:00
# define QUOTABLOCK_BITS 10
# define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
static inline qsize_t v2_stoqb ( qsize_t space )
{
return ( space + QUOTABLOCK_SIZE - 1 ) > > QUOTABLOCK_BITS ;
}
static inline qsize_t v2_qbtos ( qsize_t blocks )
{
return blocks < < QUOTABLOCK_BITS ;
}
2005-04-17 02:20:36 +04:00
/* Check whether given file is really vfsv0 quotafile */
static int v2_check_quota_file ( struct super_block * sb , int type )
{
struct v2_disk_dqheader dqhead ;
ssize_t size ;
static const uint quota_magics [ ] = V2_INITQMAGICS ;
static const uint quota_versions [ ] = V2_INITQVERSIONS ;
size = sb - > s_op - > quota_read ( sb , type , ( char * ) & dqhead , sizeof ( struct v2_disk_dqheader ) , 0 ) ;
if ( size ! = sizeof ( struct v2_disk_dqheader ) ) {
2006-02-03 14:04:03 +03:00
printk ( " quota_v2: failed read expected=%zd got=%zd \n " ,
2006-01-15 00:21:10 +03:00
sizeof ( struct v2_disk_dqheader ) , size ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
if ( le32_to_cpu ( dqhead . dqh_magic ) ! = quota_magics [ type ] | |
le32_to_cpu ( dqhead . dqh_version ) ! = quota_versions [ type ] )
return 0 ;
return 1 ;
}
/* Read information header from quota file */
static int v2_read_file_info ( struct super_block * sb , int type )
{
struct v2_disk_dqinfo dinfo ;
2008-09-22 07:54:49 +04:00
struct mem_dqinfo * info = sb_dqinfo ( sb , type ) ;
2008-10-02 20:44:14 +04:00
struct qtree_mem_dqinfo * qinfo ;
2005-04-17 02:20:36 +04:00
ssize_t size ;
size = sb - > s_op - > quota_read ( sb , type , ( char * ) & dinfo ,
sizeof ( struct v2_disk_dqinfo ) , V2_DQINFOOFF ) ;
if ( size ! = sizeof ( struct v2_disk_dqinfo ) ) {
printk ( KERN_WARNING " Can't read info structure on device %s. \n " ,
sb - > s_id ) ;
return - 1 ;
}
2008-10-02 20:44:14 +04:00
info - > dqi_priv = kmalloc ( sizeof ( struct qtree_mem_dqinfo ) , GFP_NOFS ) ;
if ( ! info - > dqi_priv ) {
printk ( KERN_WARNING
" Not enough memory for quota information structure. \n " ) ;
return - 1 ;
}
qinfo = info - > dqi_priv ;
2008-04-28 13:14:31 +04:00
/* limits are stored as unsigned 32-bit data */
info - > dqi_maxblimit = 0xffffffff ;
info - > dqi_maxilimit = 0xffffffff ;
2005-04-17 02:20:36 +04:00
info - > dqi_bgrace = le32_to_cpu ( dinfo . dqi_bgrace ) ;
info - > dqi_igrace = le32_to_cpu ( dinfo . dqi_igrace ) ;
info - > dqi_flags = le32_to_cpu ( dinfo . dqi_flags ) ;
2008-10-02 20:44:14 +04:00
qinfo - > dqi_sb = sb ;
qinfo - > dqi_type = type ;
qinfo - > dqi_blocks = le32_to_cpu ( dinfo . dqi_blocks ) ;
qinfo - > dqi_free_blk = le32_to_cpu ( dinfo . dqi_free_blk ) ;
qinfo - > dqi_free_entry = le32_to_cpu ( dinfo . dqi_free_entry ) ;
qinfo - > dqi_blocksize_bits = V2_DQBLKSIZE_BITS ;
qinfo - > dqi_usable_bs = 1 < < V2_DQBLKSIZE_BITS ;
qinfo - > dqi_qtree_depth = qtree_depth ( qinfo ) ;
qinfo - > dqi_entry_size = sizeof ( struct v2_disk_dqblk ) ;
qinfo - > dqi_ops = & v2_qtree_ops ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* Write information header to quota file */
static int v2_write_file_info ( struct super_block * sb , int type )
{
struct v2_disk_dqinfo dinfo ;
2008-09-22 07:54:49 +04:00
struct mem_dqinfo * info = sb_dqinfo ( sb , type ) ;
2008-10-02 20:44:14 +04:00
struct qtree_mem_dqinfo * qinfo = info - > dqi_priv ;
2005-04-17 02:20:36 +04:00
ssize_t size ;
spin_lock ( & dq_data_lock ) ;
info - > dqi_flags & = ~ DQF_INFO_DIRTY ;
dinfo . dqi_bgrace = cpu_to_le32 ( info - > dqi_bgrace ) ;
dinfo . dqi_igrace = cpu_to_le32 ( info - > dqi_igrace ) ;
dinfo . dqi_flags = cpu_to_le32 ( info - > dqi_flags & DQF_MASK ) ;
spin_unlock ( & dq_data_lock ) ;
2008-10-02 20:44:14 +04:00
dinfo . dqi_blocks = cpu_to_le32 ( qinfo - > dqi_blocks ) ;
dinfo . dqi_free_blk = cpu_to_le32 ( qinfo - > dqi_free_blk ) ;
dinfo . dqi_free_entry = cpu_to_le32 ( qinfo - > dqi_free_entry ) ;
2005-04-17 02:20:36 +04:00
size = sb - > s_op - > quota_write ( sb , type , ( char * ) & dinfo ,
sizeof ( struct v2_disk_dqinfo ) , V2_DQINFOOFF ) ;
if ( size ! = sizeof ( struct v2_disk_dqinfo ) ) {
printk ( KERN_WARNING " Can't write info structure on device %s. \n " ,
sb - > s_id ) ;
return - 1 ;
}
return 0 ;
}
2008-09-22 07:54:49 +04:00
static void v2_disk2memdqb ( struct dquot * dquot , void * dp )
2005-04-17 02:20:36 +04:00
{
2008-09-22 07:54:49 +04:00
struct v2_disk_dqblk * d = dp , empty ;
struct mem_dqblk * m = & dquot - > dq_dqb ;
2005-04-17 02:20:36 +04:00
m - > dqb_ihardlimit = le32_to_cpu ( d - > dqb_ihardlimit ) ;
m - > dqb_isoftlimit = le32_to_cpu ( d - > dqb_isoftlimit ) ;
m - > dqb_curinodes = le32_to_cpu ( d - > dqb_curinodes ) ;
m - > dqb_itime = le64_to_cpu ( d - > dqb_itime ) ;
2008-08-20 16:45:12 +04:00
m - > dqb_bhardlimit = v2_qbtos ( le32_to_cpu ( d - > dqb_bhardlimit ) ) ;
m - > dqb_bsoftlimit = v2_qbtos ( le32_to_cpu ( d - > dqb_bsoftlimit ) ) ;
2005-04-17 02:20:36 +04:00
m - > dqb_curspace = le64_to_cpu ( d - > dqb_curspace ) ;
m - > dqb_btime = le64_to_cpu ( d - > dqb_btime ) ;
2008-09-22 07:54:49 +04:00
/* We need to escape back all-zero structure */
memset ( & empty , 0 , sizeof ( struct v2_disk_dqblk ) ) ;
empty . dqb_itime = cpu_to_le64 ( 1 ) ;
if ( ! memcmp ( & empty , dp , sizeof ( struct v2_disk_dqblk ) ) )
m - > dqb_itime = 0 ;
2005-04-17 02:20:36 +04:00
}
2008-09-22 07:54:49 +04:00
static void v2_mem2diskdqb ( void * dp , struct dquot * dquot )
2005-04-17 02:20:36 +04:00
{
2008-09-22 07:54:49 +04:00
struct v2_disk_dqblk * d = dp ;
struct mem_dqblk * m = & dquot - > dq_dqb ;
struct qtree_mem_dqinfo * info =
2008-10-02 20:44:14 +04:00
sb_dqinfo ( dquot - > dq_sb , dquot - > dq_type ) - > dqi_priv ;
2008-09-22 07:54:49 +04:00
2005-04-17 02:20:36 +04:00
d - > dqb_ihardlimit = cpu_to_le32 ( m - > dqb_ihardlimit ) ;
d - > dqb_isoftlimit = cpu_to_le32 ( m - > dqb_isoftlimit ) ;
d - > dqb_curinodes = cpu_to_le32 ( m - > dqb_curinodes ) ;
d - > dqb_itime = cpu_to_le64 ( m - > dqb_itime ) ;
2008-09-22 01:17:53 +04:00
d - > dqb_bhardlimit = cpu_to_le32 ( v2_stoqb ( m - > dqb_bhardlimit ) ) ;
d - > dqb_bsoftlimit = cpu_to_le32 ( v2_stoqb ( m - > dqb_bsoftlimit ) ) ;
2005-04-17 02:20:36 +04:00
d - > dqb_curspace = cpu_to_le64 ( m - > dqb_curspace ) ;
d - > dqb_btime = cpu_to_le64 ( m - > dqb_btime ) ;
2008-09-22 07:54:49 +04:00
d - > dqb_id = cpu_to_le32 ( dquot - > dq_id ) ;
if ( qtree_entry_unused ( info , dp ) )
d - > dqb_itime = cpu_to_le64 ( 1 ) ;
2005-04-17 02:20:36 +04:00
}
2008-09-22 07:54:49 +04:00
static int v2_is_id ( void * dp , struct dquot * dquot )
2005-04-17 02:20:36 +04:00
{
2008-09-22 07:54:49 +04:00
struct v2_disk_dqblk * d = dp ;
struct qtree_mem_dqinfo * info =
2008-10-02 20:44:14 +04:00
sb_dqinfo ( dquot - > dq_sb , dquot - > dq_type ) - > dqi_priv ;
2005-04-17 02:20:36 +04:00
2008-09-22 07:54:49 +04:00
if ( qtree_entry_unused ( info , dp ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2008-09-22 07:54:49 +04:00
return le32_to_cpu ( d - > dqb_id ) = = dquot - > dq_id ;
2005-04-17 02:20:36 +04:00
}
2008-09-22 07:54:49 +04:00
static int v2_read_dquot ( struct dquot * dquot )
2005-04-17 02:20:36 +04:00
{
2008-10-02 20:44:14 +04:00
return qtree_read_dquot ( sb_dqinfo ( dquot - > dq_sb , dquot - > dq_type ) - > dqi_priv , dquot ) ;
2005-04-17 02:20:36 +04:00
}
static int v2_write_dquot ( struct dquot * dquot )
{
2008-10-02 20:44:14 +04:00
return qtree_write_dquot ( sb_dqinfo ( dquot - > dq_sb , dquot - > dq_type ) - > dqi_priv , dquot ) ;
2005-04-17 02:20:36 +04:00
}
static int v2_release_dquot ( struct dquot * dquot )
{
2008-10-02 20:44:14 +04:00
return qtree_release_dquot ( sb_dqinfo ( dquot - > dq_sb , dquot - > dq_type ) - > dqi_priv , dquot ) ;
}
static int v2_free_file_info ( struct super_block * sb , int type )
{
kfree ( sb_dqinfo ( sb , type ) - > dqi_priv ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static struct quota_format_ops v2_format_ops = {
. check_quota_file = v2_check_quota_file ,
. read_file_info = v2_read_file_info ,
. write_file_info = v2_write_file_info ,
2008-10-02 20:44:14 +04:00
. free_file_info = v2_free_file_info ,
2005-04-17 02:20:36 +04:00
. read_dqblk = v2_read_dquot ,
. commit_dqblk = v2_write_dquot ,
. release_dqblk = v2_release_dquot ,
} ;
static struct quota_format_type v2_quota_format = {
. qf_fmt_id = QFMT_VFS_V0 ,
. qf_ops = & v2_format_ops ,
. qf_owner = THIS_MODULE
} ;
static int __init init_v2_quota_format ( void )
{
return register_quota_format ( & v2_quota_format ) ;
}
static void __exit exit_v2_quota_format ( void )
{
unregister_quota_format ( & v2_quota_format ) ;
}
module_init ( init_v2_quota_format ) ;
module_exit ( exit_v2_quota_format ) ;