quota_v1.c 6.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/quota.h>
4
#include <linux/quotaops.h>
L
Linus Torvalds 已提交
5 6 7 8 9 10 11
#include <linux/dqblk_v1.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>

#include <asm/byteorder.h>

12 13
#include "quotaio_v1.h"

L
Linus Torvalds 已提交
14 15 16 17
MODULE_AUTHOR("Jan Kara");
MODULE_DESCRIPTION("Old quota format support");
MODULE_LICENSE("GPL");

18 19 20 21 22 23 24 25 26 27 28 29 30
#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;
}

L
Linus Torvalds 已提交
31 32 33 34 35
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;
36 37 38
	m->dqb_bhardlimit = v1_qbtos(d->dqb_bhardlimit);
	m->dqb_bsoftlimit = v1_qbtos(d->dqb_bsoftlimit);
	m->dqb_curspace = v1_qbtos(d->dqb_curblocks);
L
Linus Torvalds 已提交
39 40 41 42 43 44 45 46 47
	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;
48 49 50
	d->dqb_bhardlimit = v1_stoqb(m->dqb_bhardlimit);
	d->dqb_bsoftlimit = v1_stoqb(m->dqb_bsoftlimit);
	d->dqb_curblocks = v1_stoqb(m->dqb_curspace);
L
Linus Torvalds 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64
	d->dqb_itime = m->dqb_itime;
	d->dqb_btime = m->dqb_btime;
}

static int v1_read_dqblk(struct dquot *dquot)
{
	int type = dquot->dq_type;
	struct v1_disk_dqblk dqblk;

	if (!sb_dqopt(dquot->dq_sb)->files[type])
		return -EINVAL;

	/* Set structure to 0s in case read fails/is after end of file */
	memset(&dqblk, 0, sizeof(struct v1_disk_dqblk));
J
Jan Kara 已提交
65 66
	dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk,
			sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id));
L
Linus Torvalds 已提交
67 68

	v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
J
Jan Kara 已提交
69 70 71 72
	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)
L
Linus Torvalds 已提交
73
		set_bit(DQ_FAKE_B, &dquot->dq_flags);
74
	dqstats_inc(DQST_READS);
L
Linus Torvalds 已提交
75 76 77 78 79 80 81 82 83 84 85 86

	return 0;
}

static int v1_commit_dqblk(struct dquot *dquot)
{
	short type = dquot->dq_type;
	ssize_t ret;
	struct v1_disk_dqblk dqblk;

	v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
	if (dquot->dq_id == 0) {
J
Jan Kara 已提交
87 88 89 90
		dqblk.dqb_btime =
			sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
		dqblk.dqb_itime =
			sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace;
L
Linus Torvalds 已提交
91 92 93
	}
	ret = 0;
	if (sb_dqopt(dquot->dq_sb)->files[type])
J
Jan Kara 已提交
94 95 96
		ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
			(char *)&dqblk, sizeof(struct v1_disk_dqblk),
			v1_dqoff(dquot->dq_id));
L
Linus Torvalds 已提交
97 98 99 100 101 102 103 104 105 106
	if (ret != sizeof(struct v1_disk_dqblk)) {
		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
			dquot->dq_sb->s_id);
		if (ret >= 0)
			ret = -EIO;
		goto out;
	}
	ret = 0;

out:
107
	dqstats_inc(DQST_WRITES);
L
Linus Torvalds 已提交
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

	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;
	size_t off; 
	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);
J
Jan Kara 已提交
139 140
	if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) %
	    sizeof(struct v1_disk_dqblk))
L
Linus Torvalds 已提交
141
		return 0;
J
Jan Kara 已提交
142 143 144 145
	/* 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);
L
Linus Torvalds 已提交
146 147 148 149
	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 */
J
Jan Kara 已提交
150 151 152
	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);
L
Linus Torvalds 已提交
153 154 155 156 157 158 159 160 161
        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;

J
Jan Kara 已提交
162 163 164
	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)) {
L
Linus Torvalds 已提交
165 166 167 168 169
		if (ret >= 0)
			ret = -EIO;
		goto out;
	}
	ret = 0;
170 171 172
	/* limits are stored as unsigned 32-bit data */
	dqopt->info[type].dqi_maxblimit = 0xffffffff;
	dqopt->info[type].dqi_maxilimit = 0xffffffff;
J
Jan Kara 已提交
173 174 175 176
	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;
L
Linus Torvalds 已提交
177 178 179 180 181 182 183 184 185 186 187
out:
	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;

	dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY;
J
Jan Kara 已提交
188 189 190
	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)) {
L
Linus Torvalds 已提交
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
		if (ret >= 0)
			ret = -EIO;
		goto out;
	}
	dqblk.dqb_itime = dqopt->info[type].dqi_igrace;
	dqblk.dqb_btime = dqopt->info[type].dqi_bgrace;
	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:
	return ret;
}

A
Alexey Dobriyan 已提交
207
static const struct quota_format_ops v1_format_ops = {
L
Linus Torvalds 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
	.check_quota_file	= v1_check_quota_file,
	.read_file_info		= v1_read_file_info,
	.write_file_info	= v1_write_file_info,
	.free_file_info		= NULL,
	.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);