1#include <linux/errno.h> 2#include <linux/fs.h> 3#include <linux/quota.h> 4#include <linux/dqblk_v1.h> 5#include <linux/quotaio_v1.h> 6#include <linux/kernel.h> 7#include <linux/init.h> 8#include <linux/module.h> 9 10#include <asm/byteorder.h> 11 12MODULE_AUTHOR("Jan Kara"); 13MODULE_DESCRIPTION("Old quota format support"); 14MODULE_LICENSE("GPL"); 15 16static void v1_disk2mem_dqblk(struct mem_dqblk *m, struct v1_disk_dqblk *d) 17{ 18 m->dqb_ihardlimit = d->dqb_ihardlimit; 19 m->dqb_isoftlimit = d->dqb_isoftlimit; 20 m->dqb_curinodes = d->dqb_curinodes; 21 m->dqb_bhardlimit = d->dqb_bhardlimit; 22 m->dqb_bsoftlimit = d->dqb_bsoftlimit; 23 m->dqb_curspace = ((qsize_t)d->dqb_curblocks) << QUOTABLOCK_BITS; 24 m->dqb_itime = d->dqb_itime; 25 m->dqb_btime = d->dqb_btime; 26} 27 28static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m) 29{ 30 d->dqb_ihardlimit = m->dqb_ihardlimit; 31 d->dqb_isoftlimit = m->dqb_isoftlimit; 32 d->dqb_curinodes = m->dqb_curinodes; 33 d->dqb_bhardlimit = m->dqb_bhardlimit; 34 d->dqb_bsoftlimit = m->dqb_bsoftlimit; 35 d->dqb_curblocks = toqb(m->dqb_curspace); 36 d->dqb_itime = m->dqb_itime; 37 d->dqb_btime = m->dqb_btime; 38} 39 40static int v1_read_dqblk(struct dquot *dquot) 41{ 42 int type = dquot->dq_type; 43 struct v1_disk_dqblk dqblk; 44 45 if (!sb_dqopt(dquot->dq_sb)->files[type]) 46 return -EINVAL; 47 48 /* Set structure to 0s in case read fails/is after end of file */ 49 memset(&dqblk, 0, sizeof(struct v1_disk_dqblk)); 50 dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id)); 51 52 v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk); 53 if (dquot->dq_dqb.dqb_bhardlimit == 0 && dquot->dq_dqb.dqb_bsoftlimit == 0 && 54 dquot->dq_dqb.dqb_ihardlimit == 0 && dquot->dq_dqb.dqb_isoftlimit == 0) 55 set_bit(DQ_FAKE_B, &dquot->dq_flags); 56 dqstats.reads++; 57 58 return 0; 59} 60 61static int v1_commit_dqblk(struct dquot *dquot) 62{ 63 short type = dquot->dq_type; 64 ssize_t ret; 65 struct v1_disk_dqblk dqblk; 66 67 v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb); 68 if (dquot->dq_id == 0) { 69 dqblk.dqb_btime = sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace; 70 dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace; 71 } 72 ret = 0; 73 if (sb_dqopt(dquot->dq_sb)->files[type]) 74 ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type, (char *)&dqblk, 75 sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id)); 76 if (ret != sizeof(struct v1_disk_dqblk)) { 77 printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", 78 dquot->dq_sb->s_id); 79 if (ret >= 0) 80 ret = -EIO; 81 goto out; 82 } 83 ret = 0; 84 85out: 86 dqstats.writes++; 87 88 return ret; 89} 90 91/* Magics of new quota format */ 92#define V2_INITQMAGICS {\ 93 0xd9c01f11, /* USRQUOTA */\ 94 0xd9c01927 /* GRPQUOTA */\ 95} 96 97/* Header of new quota format */ 98struct v2_disk_dqheader { 99 __le32 dqh_magic; /* Magic number identifying file */ 100 __le32 dqh_version; /* File version */ 101}; 102 103static int v1_check_quota_file(struct super_block *sb, int type) 104{ 105 struct inode *inode = sb_dqopt(sb)->files[type]; 106 ulong blocks; 107 size_t off; 108 struct v2_disk_dqheader dqhead; 109 ssize_t size; 110 loff_t isize; 111 static const uint quota_magics[] = V2_INITQMAGICS; 112 113 isize = i_size_read(inode); 114 if (!isize) 115 return 0; 116 blocks = isize >> BLOCK_SIZE_BITS; 117 off = isize & (BLOCK_SIZE - 1); 118 if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) % sizeof(struct v1_disk_dqblk)) 119 return 0; 120 /* Doublecheck whether we didn't get file with new format - with old quotactl() this could happen */ 121 size = sb->s_op->quota_read(sb, type, (char *)&dqhead, sizeof(struct v2_disk_dqheader), 0); 122 if (size != sizeof(struct v2_disk_dqheader)) 123 return 1; /* Probably not new format */ 124 if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type]) 125 return 1; /* Definitely not new format */ 126 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); 127 return 0; /* Seems like a new format file -> refuse it */ 128} 129 130static int v1_read_file_info(struct super_block *sb, int type) 131{ 132 struct quota_info *dqopt = sb_dqopt(sb); 133 struct v1_disk_dqblk dqblk; 134 int ret; 135 136 if ((ret = sb->s_op->quota_read(sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(0))) != sizeof(struct v1_disk_dqblk)) { 137 if (ret >= 0) 138 ret = -EIO; 139 goto out; 140 } 141 ret = 0; 142 dqopt->info[type].dqi_igrace = dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME; 143 dqopt->info[type].dqi_bgrace = dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME; 144out: 145 return ret; 146} 147 148static int v1_write_file_info(struct super_block *sb, int type) 149{ 150 struct quota_info *dqopt = sb_dqopt(sb); 151 struct v1_disk_dqblk dqblk; 152 int ret; 153 154 dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY; 155 if ((ret = sb->s_op->quota_read(sb, type, (char *)&dqblk, 156 sizeof(struct v1_disk_dqblk), v1_dqoff(0))) != sizeof(struct v1_disk_dqblk)) { 157 if (ret >= 0) 158 ret = -EIO; 159 goto out; 160 } 161 dqblk.dqb_itime = dqopt->info[type].dqi_igrace; 162 dqblk.dqb_btime = dqopt->info[type].dqi_bgrace; 163 ret = sb->s_op->quota_write(sb, type, (char *)&dqblk, 164 sizeof(struct v1_disk_dqblk), v1_dqoff(0)); 165 if (ret == sizeof(struct v1_disk_dqblk)) 166 ret = 0; 167 else if (ret > 0) 168 ret = -EIO; 169out: 170 return ret; 171} 172 173static struct quota_format_ops v1_format_ops = { 174 .check_quota_file = v1_check_quota_file, 175 .read_file_info = v1_read_file_info, 176 .write_file_info = v1_write_file_info, 177 .free_file_info = NULL, 178 .read_dqblk = v1_read_dqblk, 179 .commit_dqblk = v1_commit_dqblk, 180}; 181 182static struct quota_format_type v1_quota_format = { 183 .qf_fmt_id = QFMT_VFS_OLD, 184 .qf_ops = &v1_format_ops, 185 .qf_owner = THIS_MODULE 186}; 187 188static int __init init_v1_quota_format(void) 189{ 190 return register_quota_format(&v1_quota_format); 191} 192 193static void __exit exit_v1_quota_format(void) 194{ 195 unregister_quota_format(&v1_quota_format); 196} 197 198module_init(init_v1_quota_format); 199module_exit(exit_v1_quota_format); 200