0001
0002 #include <linux/errno.h>
0003 #include <linux/fs.h>
0004 #include <linux/quota.h>
0005 #include <linux/quotaops.h>
0006 #include <linux/dqblk_v1.h>
0007 #include <linux/kernel.h>
0008 #include <linux/init.h>
0009 #include <linux/module.h>
0010
0011 #include <asm/byteorder.h>
0012
0013 #include "quotaio_v1.h"
0014
0015 MODULE_AUTHOR("Jan Kara");
0016 MODULE_DESCRIPTION("Old quota format support");
0017 MODULE_LICENSE("GPL");
0018
0019 #define QUOTABLOCK_BITS 10
0020 #define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
0021
0022 static inline qsize_t v1_stoqb(qsize_t space)
0023 {
0024 return (space + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS;
0025 }
0026
0027 static inline qsize_t v1_qbtos(qsize_t blocks)
0028 {
0029 return blocks << QUOTABLOCK_BITS;
0030 }
0031
0032 static void v1_disk2mem_dqblk(struct mem_dqblk *m, struct v1_disk_dqblk *d)
0033 {
0034 m->dqb_ihardlimit = d->dqb_ihardlimit;
0035 m->dqb_isoftlimit = d->dqb_isoftlimit;
0036 m->dqb_curinodes = d->dqb_curinodes;
0037 m->dqb_bhardlimit = v1_qbtos(d->dqb_bhardlimit);
0038 m->dqb_bsoftlimit = v1_qbtos(d->dqb_bsoftlimit);
0039 m->dqb_curspace = v1_qbtos(d->dqb_curblocks);
0040 m->dqb_itime = d->dqb_itime;
0041 m->dqb_btime = d->dqb_btime;
0042 }
0043
0044 static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)
0045 {
0046 d->dqb_ihardlimit = m->dqb_ihardlimit;
0047 d->dqb_isoftlimit = m->dqb_isoftlimit;
0048 d->dqb_curinodes = m->dqb_curinodes;
0049 d->dqb_bhardlimit = v1_stoqb(m->dqb_bhardlimit);
0050 d->dqb_bsoftlimit = v1_stoqb(m->dqb_bsoftlimit);
0051 d->dqb_curblocks = v1_stoqb(m->dqb_curspace);
0052 d->dqb_itime = m->dqb_itime;
0053 d->dqb_btime = m->dqb_btime;
0054 }
0055
0056 static int v1_read_dqblk(struct dquot *dquot)
0057 {
0058 int type = dquot->dq_id.type;
0059 struct v1_disk_dqblk dqblk;
0060 struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
0061
0062 if (!dqopt->files[type])
0063 return -EINVAL;
0064
0065
0066 memset(&dqblk, 0, sizeof(struct v1_disk_dqblk));
0067 dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk,
0068 sizeof(struct v1_disk_dqblk),
0069 v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id)));
0070
0071 v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
0072 if (dquot->dq_dqb.dqb_bhardlimit == 0 &&
0073 dquot->dq_dqb.dqb_bsoftlimit == 0 &&
0074 dquot->dq_dqb.dqb_ihardlimit == 0 &&
0075 dquot->dq_dqb.dqb_isoftlimit == 0)
0076 set_bit(DQ_FAKE_B, &dquot->dq_flags);
0077 dqstats_inc(DQST_READS);
0078
0079 return 0;
0080 }
0081
0082 static int v1_commit_dqblk(struct dquot *dquot)
0083 {
0084 short type = dquot->dq_id.type;
0085 ssize_t ret;
0086 struct v1_disk_dqblk dqblk;
0087
0088 v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
0089 if (((type == USRQUOTA) && uid_eq(dquot->dq_id.uid, GLOBAL_ROOT_UID)) ||
0090 ((type == GRPQUOTA) && gid_eq(dquot->dq_id.gid, GLOBAL_ROOT_GID))) {
0091 dqblk.dqb_btime =
0092 sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
0093 dqblk.dqb_itime =
0094 sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace;
0095 }
0096 ret = 0;
0097 if (sb_dqopt(dquot->dq_sb)->files[type])
0098 ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
0099 (char *)&dqblk, sizeof(struct v1_disk_dqblk),
0100 v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id)));
0101 if (ret != sizeof(struct v1_disk_dqblk)) {
0102 quota_error(dquot->dq_sb, "dquota write failed");
0103 if (ret >= 0)
0104 ret = -EIO;
0105 goto out;
0106 }
0107 ret = 0;
0108
0109 out:
0110 dqstats_inc(DQST_WRITES);
0111
0112 return ret;
0113 }
0114
0115
0116 #define V2_INITQMAGICS {\
0117 0xd9c01f11, \
0118 0xd9c01927 \
0119 }
0120
0121
0122 struct v2_disk_dqheader {
0123 __le32 dqh_magic;
0124 __le32 dqh_version;
0125 };
0126
0127 static int v1_check_quota_file(struct super_block *sb, int type)
0128 {
0129 struct inode *inode = sb_dqopt(sb)->files[type];
0130 ulong blocks;
0131 size_t off;
0132 struct v2_disk_dqheader dqhead;
0133 ssize_t size;
0134 loff_t isize;
0135 static const uint quota_magics[] = V2_INITQMAGICS;
0136
0137 isize = i_size_read(inode);
0138 if (!isize)
0139 return 0;
0140 blocks = isize >> BLOCK_SIZE_BITS;
0141 off = isize & (BLOCK_SIZE - 1);
0142 if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) %
0143 sizeof(struct v1_disk_dqblk))
0144 return 0;
0145
0146
0147 size = sb->s_op->quota_read(sb, type, (char *)&dqhead,
0148 sizeof(struct v2_disk_dqheader), 0);
0149 if (size != sizeof(struct v2_disk_dqheader))
0150 return 1;
0151 if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
0152 return 1;
0153 printk(KERN_INFO
0154 "VFS: %s: Refusing to turn on old quota format on given file."
0155 " It probably contains newer quota format.\n", sb->s_id);
0156 return 0;
0157 }
0158
0159 static int v1_read_file_info(struct super_block *sb, int type)
0160 {
0161 struct quota_info *dqopt = sb_dqopt(sb);
0162 struct v1_disk_dqblk dqblk;
0163 int ret;
0164
0165 down_read(&dqopt->dqio_sem);
0166 ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
0167 sizeof(struct v1_disk_dqblk), v1_dqoff(0));
0168 if (ret != sizeof(struct v1_disk_dqblk)) {
0169 if (ret >= 0)
0170 ret = -EIO;
0171 goto out;
0172 }
0173 ret = 0;
0174
0175 dqopt->info[type].dqi_max_spc_limit = 0xffffffffULL << QUOTABLOCK_BITS;
0176 dqopt->info[type].dqi_max_ino_limit = 0xffffffff;
0177 dqopt->info[type].dqi_igrace =
0178 dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME;
0179 dqopt->info[type].dqi_bgrace =
0180 dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
0181 out:
0182 up_read(&dqopt->dqio_sem);
0183 return ret;
0184 }
0185
0186 static int v1_write_file_info(struct super_block *sb, int type)
0187 {
0188 struct quota_info *dqopt = sb_dqopt(sb);
0189 struct v1_disk_dqblk dqblk;
0190 int ret;
0191
0192 down_write(&dqopt->dqio_sem);
0193 ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
0194 sizeof(struct v1_disk_dqblk), v1_dqoff(0));
0195 if (ret != sizeof(struct v1_disk_dqblk)) {
0196 if (ret >= 0)
0197 ret = -EIO;
0198 goto out;
0199 }
0200 spin_lock(&dq_data_lock);
0201 dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY;
0202 dqblk.dqb_itime = dqopt->info[type].dqi_igrace;
0203 dqblk.dqb_btime = dqopt->info[type].dqi_bgrace;
0204 spin_unlock(&dq_data_lock);
0205 ret = sb->s_op->quota_write(sb, type, (char *)&dqblk,
0206 sizeof(struct v1_disk_dqblk), v1_dqoff(0));
0207 if (ret == sizeof(struct v1_disk_dqblk))
0208 ret = 0;
0209 else if (ret > 0)
0210 ret = -EIO;
0211 out:
0212 up_write(&dqopt->dqio_sem);
0213 return ret;
0214 }
0215
0216 static const struct quota_format_ops v1_format_ops = {
0217 .check_quota_file = v1_check_quota_file,
0218 .read_file_info = v1_read_file_info,
0219 .write_file_info = v1_write_file_info,
0220 .read_dqblk = v1_read_dqblk,
0221 .commit_dqblk = v1_commit_dqblk,
0222 };
0223
0224 static struct quota_format_type v1_quota_format = {
0225 .qf_fmt_id = QFMT_VFS_OLD,
0226 .qf_ops = &v1_format_ops,
0227 .qf_owner = THIS_MODULE
0228 };
0229
0230 static int __init init_v1_quota_format(void)
0231 {
0232 return register_quota_format(&v1_quota_format);
0233 }
0234
0235 static void __exit exit_v1_quota_format(void)
0236 {
0237 unregister_quota_format(&v1_quota_format);
0238 }
0239
0240 module_init(init_v1_quota_format);
0241 module_exit(exit_v1_quota_format);
0242