quota_v2.c 10.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/*
 *	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>
13
#include <linux/quotaops.h>
L
Linus Torvalds 已提交
14 15 16

#include <asm/byteorder.h>

17
#include "quota_tree.h"
18 19
#include "quotaio_v2.h"

L
Linus Torvalds 已提交
20 21 22 23 24 25
MODULE_AUTHOR("Jan Kara");
MODULE_DESCRIPTION("Quota format v2 support");
MODULE_LICENSE("GPL");

#define __QUOTA_V2_PARANOIA

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot);
static void v2r0_disk2memdqb(struct dquot *dquot, void *dp);
static int v2r0_is_id(void *dp, struct dquot *dquot);
static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot);
static void v2r1_disk2memdqb(struct dquot *dquot, void *dp);
static int v2r1_is_id(void *dp, struct dquot *dquot);

static struct qtree_fmt_operations v2r0_qtree_ops = {
	.mem2disk_dqblk = v2r0_mem2diskdqb,
	.disk2mem_dqblk = v2r0_disk2memdqb,
	.is_id = v2r0_is_id,
};

static struct qtree_fmt_operations v2r1_qtree_ops = {
	.mem2disk_dqblk = v2r1_mem2diskdqb,
	.disk2mem_dqblk = v2r1_disk2memdqb,
	.is_id = v2r1_is_id,
43
};
L
Linus Torvalds 已提交
44

45 46 47 48 49 50 51 52 53 54 55 56 57
#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;
}

58 59 60 61 62 63 64 65
static int v2_read_header(struct super_block *sb, int type,
			  struct v2_disk_dqheader *dqhead)
{
	ssize_t size;

	size = sb->s_op->quota_read(sb, type, (char *)dqhead,
				    sizeof(struct v2_disk_dqheader), 0);
	if (size != sizeof(struct v2_disk_dqheader)) {
66
		q_warn(KERN_WARNING "quota_v2: Failed header read:"
67 68 69 70 71 72 73
		       " expected=%zd got=%zd\n",
			sizeof(struct v2_disk_dqheader), size);
		return 0;
	}
	return 1;
}

L
Linus Torvalds 已提交
74 75 76 77 78 79 80
/* 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;
	static const uint quota_magics[] = V2_INITQMAGICS;
	static const uint quota_versions[] = V2_INITQVERSIONS;
 
81
	if (!v2_read_header(sb, type, &dqhead))
L
Linus Torvalds 已提交
82 83
		return 0;
	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
84
	    le32_to_cpu(dqhead.dqh_version) > quota_versions[type])
L
Linus Torvalds 已提交
85 86 87 88 89 90 91 92
		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;
93
	struct v2_disk_dqheader dqhead;
94
	struct mem_dqinfo *info = sb_dqinfo(sb, type);
95
	struct qtree_mem_dqinfo *qinfo;
L
Linus Torvalds 已提交
96
	ssize_t size;
97 98 99
	unsigned int version;

	if (!v2_read_header(sb, type, &dqhead))
100
		return -1;
101
	version = le32_to_cpu(dqhead.dqh_version);
102 103 104
	if ((info->dqi_fmt_id == QFMT_VFS_V0 && version != 0) ||
	    (info->dqi_fmt_id == QFMT_VFS_V1 && version != 1))
		return -1;
L
Linus Torvalds 已提交
105 106 107 108

	size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
	if (size != sizeof(struct v2_disk_dqinfo)) {
109
		q_warn(KERN_WARNING "quota_v2: Can't read info structure on device %s.\n",
L
Linus Torvalds 已提交
110 111 112
			sb->s_id);
		return -1;
	}
113 114 115 116 117 118 119
	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;
120 121 122 123 124 125
	if (version == 0) {
		/* limits are stored as unsigned 32-bit data */
		info->dqi_maxblimit = 0xffffffff;
		info->dqi_maxilimit = 0xffffffff;
	} else {
		/* used space is stored as unsigned 64-bit value */
126 127
		info->dqi_maxblimit = 0xffffffffffffffffULL;	/* 2^64-1 */
		info->dqi_maxilimit = 0xffffffffffffffffULL;
128
	}
L
Linus Torvalds 已提交
129 130 131
	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);
132 133 134 135 136 137 138 139
	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);
140 141 142 143 144 145 146
	if (version == 0) {
		qinfo->dqi_entry_size = sizeof(struct v2r0_disk_dqblk);
		qinfo->dqi_ops = &v2r0_qtree_ops;
	} else {
		qinfo->dqi_entry_size = sizeof(struct v2r1_disk_dqblk);
		qinfo->dqi_ops = &v2r1_qtree_ops;
	}
L
Linus Torvalds 已提交
147 148 149 150 151 152 153
	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;
154
	struct mem_dqinfo *info = sb_dqinfo(sb, type);
155
	struct qtree_mem_dqinfo *qinfo = info->dqi_priv;
L
Linus Torvalds 已提交
156 157 158 159 160 161 162 163
	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);
164 165 166
	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);
L
Linus Torvalds 已提交
167 168 169
	size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
	if (size != sizeof(struct v2_disk_dqinfo)) {
170
		q_warn(KERN_WARNING "Can't write info structure on device %s.\n",
L
Linus Torvalds 已提交
171 172 173 174 175 176
			sb->s_id);
		return -1;
	}
	return 0;
}

177
static void v2r0_disk2memdqb(struct dquot *dquot, void *dp)
L
Linus Torvalds 已提交
178
{
179
	struct v2r0_disk_dqblk *d = dp, empty;
180 181
	struct mem_dqblk *m = &dquot->dq_dqb;

L
Linus Torvalds 已提交
182 183 184 185
	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);
186 187
	m->dqb_bhardlimit = v2_qbtos(le32_to_cpu(d->dqb_bhardlimit));
	m->dqb_bsoftlimit = v2_qbtos(le32_to_cpu(d->dqb_bsoftlimit));
L
Linus Torvalds 已提交
188 189
	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
	m->dqb_btime = le64_to_cpu(d->dqb_btime);
190
	/* We need to escape back all-zero structure */
191
	memset(&empty, 0, sizeof(struct v2r0_disk_dqblk));
192
	empty.dqb_itime = cpu_to_le64(1);
193
	if (!memcmp(&empty, dp, sizeof(struct v2r0_disk_dqblk)))
194
		m->dqb_itime = 0;
L
Linus Torvalds 已提交
195 196
}

197
static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
L
Linus Torvalds 已提交
198
{
199
	struct v2r0_disk_dqblk *d = dp;
200 201
	struct mem_dqblk *m = &dquot->dq_dqb;
	struct qtree_mem_dqinfo *info =
202
			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
203

L
Linus Torvalds 已提交
204 205 206 207
	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);
208 209
	d->dqb_bhardlimit = cpu_to_le32(v2_stoqb(m->dqb_bhardlimit));
	d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit));
L
Linus Torvalds 已提交
210 211
	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
	d->dqb_btime = cpu_to_le64(m->dqb_btime);
212 213 214
	d->dqb_id = cpu_to_le32(dquot->dq_id);
	if (qtree_entry_unused(info, dp))
		d->dqb_itime = cpu_to_le64(1);
L
Linus Torvalds 已提交
215 216
}

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
static int v2r0_is_id(void *dp, struct dquot *dquot)
{
	struct v2r0_disk_dqblk *d = dp;
	struct qtree_mem_dqinfo *info =
			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;

	if (qtree_entry_unused(info, dp))
		return 0;
	return le32_to_cpu(d->dqb_id) == dquot->dq_id;
}

static void v2r1_disk2memdqb(struct dquot *dquot, void *dp)
{
	struct v2r1_disk_dqblk *d = dp, empty;
	struct mem_dqblk *m = &dquot->dq_dqb;

	m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
	m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
	m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
	m->dqb_itime = le64_to_cpu(d->dqb_itime);
	m->dqb_bhardlimit = v2_qbtos(le64_to_cpu(d->dqb_bhardlimit));
	m->dqb_bsoftlimit = v2_qbtos(le64_to_cpu(d->dqb_bsoftlimit));
	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
	m->dqb_btime = le64_to_cpu(d->dqb_btime);
	/* We need to escape back all-zero structure */
	memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
	empty.dqb_itime = cpu_to_le64(1);
	if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))
		m->dqb_itime = 0;
}

static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
{
	struct v2r1_disk_dqblk *d = dp;
	struct mem_dqblk *m = &dquot->dq_dqb;
	struct qtree_mem_dqinfo *info =
			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;

	d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
	d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
	d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
	d->dqb_itime = cpu_to_le64(m->dqb_itime);
	d->dqb_bhardlimit = cpu_to_le64(v2_stoqb(m->dqb_bhardlimit));
	d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit));
	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
	d->dqb_btime = cpu_to_le64(m->dqb_btime);
	d->dqb_id = cpu_to_le32(dquot->dq_id);
	if (qtree_entry_unused(info, dp))
		d->dqb_itime = cpu_to_le64(1);
}

static int v2r1_is_id(void *dp, struct dquot *dquot)
L
Linus Torvalds 已提交
269
{
270
	struct v2r1_disk_dqblk *d = dp;
271
	struct qtree_mem_dqinfo *info =
272
			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
L
Linus Torvalds 已提交
273

274
	if (qtree_entry_unused(info, dp))
L
Linus Torvalds 已提交
275
		return 0;
276
	return le32_to_cpu(d->dqb_id) == dquot->dq_id;
L
Linus Torvalds 已提交
277 278
}

279
static int v2_read_dquot(struct dquot *dquot)
L
Linus Torvalds 已提交
280
{
281
	return qtree_read_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
L
Linus Torvalds 已提交
282 283 284 285
}

static int v2_write_dquot(struct dquot *dquot)
{
286
	return qtree_write_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
L
Linus Torvalds 已提交
287 288 289 290
}

static int v2_release_dquot(struct dquot *dquot)
{
291 292 293 294 295 296 297
	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;
L
Linus Torvalds 已提交
298 299
}

A
Alexey Dobriyan 已提交
300
static const struct quota_format_ops v2_format_ops = {
L
Linus Torvalds 已提交
301 302 303
	.check_quota_file	= v2_check_quota_file,
	.read_file_info		= v2_read_file_info,
	.write_file_info	= v2_write_file_info,
304
	.free_file_info		= v2_free_file_info,
L
Linus Torvalds 已提交
305 306 307 308 309
	.read_dqblk		= v2_read_dquot,
	.commit_dqblk		= v2_write_dquot,
	.release_dqblk		= v2_release_dquot,
};

310
static struct quota_format_type v2r0_quota_format = {
L
Linus Torvalds 已提交
311 312 313 314 315
	.qf_fmt_id	= QFMT_VFS_V0,
	.qf_ops		= &v2_format_ops,
	.qf_owner	= THIS_MODULE
};

316 317 318 319 320 321
static struct quota_format_type v2r1_quota_format = {
	.qf_fmt_id	= QFMT_VFS_V1,
	.qf_ops		= &v2_format_ops,
	.qf_owner	= THIS_MODULE
};

L
Linus Torvalds 已提交
322 323
static int __init init_v2_quota_format(void)
{
324 325 326 327 328 329
	int ret;

	ret = register_quota_format(&v2r0_quota_format);
	if (ret)
		return ret;
	return register_quota_format(&v2r1_quota_format);
L
Linus Torvalds 已提交
330 331 332 333
}

static void __exit exit_v2_quota_format(void)
{
334 335
	unregister_quota_format(&v2r0_quota_format);
	unregister_quota_format(&v2r1_quota_format);
L
Linus Torvalds 已提交
336 337 338 339
}

module_init(init_v2_quota_format);
module_exit(exit_v2_quota_format);