quota_v2.c 10.1 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 66 67 68 69 70 71 72 73
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)) {
		printk(KERN_WARNING "quota_v2: Failed header read:"
		       " 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 100 101
	unsigned int version;

	if (!v2_read_header(sb, type, &dqhead))
		return 0;
	version = le32_to_cpu(dqhead.dqh_version);
L
Linus Torvalds 已提交
102 103 104 105

	size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
	if (size != sizeof(struct v2_disk_dqinfo)) {
106
		printk(KERN_WARNING "quota_v2: Can't read info structure on device %s.\n",
L
Linus Torvalds 已提交
107 108 109
			sb->s_id);
		return -1;
	}
110 111 112 113 114 115 116
	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;
117 118 119 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 */
		info->dqi_maxblimit = 0xffffffffffffffff;	/* 2^64-1 */
		info->dqi_maxilimit = 0xffffffffffffffff;
	}
L
Linus Torvalds 已提交
126 127 128
	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);
129 130 131 132 133 134 135 136
	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);
137 138 139 140 141 142 143
	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 已提交
144 145 146 147 148 149 150
	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;
151
	struct mem_dqinfo *info = sb_dqinfo(sb, type);
152
	struct qtree_mem_dqinfo *qinfo = info->dqi_priv;
L
Linus Torvalds 已提交
153 154 155 156 157 158 159 160
	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);
161 162 163
	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 已提交
164 165 166 167 168 169 170 171 172 173
	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;
}

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

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

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

L
Linus Torvalds 已提交
201 202 203 204
	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);
205 206
	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 已提交
207 208
	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
	d->dqb_btime = cpu_to_le64(m->dqb_btime);
209 210 211
	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 已提交
212 213
}

214 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
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 已提交
266
{
267
	struct v2r1_disk_dqblk *d = dp;
268
	struct qtree_mem_dqinfo *info =
269
			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
L
Linus Torvalds 已提交
270

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

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

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

static int v2_release_dquot(struct dquot *dquot)
{
288 289 290 291 292 293 294
	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 已提交
295 296
}

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

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

313 314 315 316 317 318
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 已提交
319 320
static int __init init_v2_quota_format(void)
{
321 322 323 324 325 326
	int ret;

	ret = register_quota_format(&v2r0_quota_format);
	if (ret)
		return ret;
	return register_quota_format(&v2r1_quota_format);
L
Linus Torvalds 已提交
327 328 329 330
}

static void __exit exit_v2_quota_format(void)
{
331 332
	unregister_quota_format(&v2r0_quota_format);
	unregister_quota_format(&v2r1_quota_format);
L
Linus Torvalds 已提交
333 334 335 336
}

module_init(init_v2_quota_format);
module_exit(exit_v2_quota_format);