18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	vfsv0 quota IO operations on file
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/errno.h>
78c2ecf20Sopenharmony_ci#include <linux/fs.h>
88c2ecf20Sopenharmony_ci#include <linux/mount.h>
98c2ecf20Sopenharmony_ci#include <linux/dqblk_v2.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/quotaops.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "quota_tree.h"
198c2ecf20Sopenharmony_ci#include "quotaio_v2.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jan Kara");
228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Quota format v2 support");
238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic void v2r0_mem2diskdqb(void *dp, struct dquot *dquot);
268c2ecf20Sopenharmony_cistatic void v2r0_disk2memdqb(struct dquot *dquot, void *dp);
278c2ecf20Sopenharmony_cistatic int v2r0_is_id(void *dp, struct dquot *dquot);
288c2ecf20Sopenharmony_cistatic void v2r1_mem2diskdqb(void *dp, struct dquot *dquot);
298c2ecf20Sopenharmony_cistatic void v2r1_disk2memdqb(struct dquot *dquot, void *dp);
308c2ecf20Sopenharmony_cistatic int v2r1_is_id(void *dp, struct dquot *dquot);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic const struct qtree_fmt_operations v2r0_qtree_ops = {
338c2ecf20Sopenharmony_ci	.mem2disk_dqblk = v2r0_mem2diskdqb,
348c2ecf20Sopenharmony_ci	.disk2mem_dqblk = v2r0_disk2memdqb,
358c2ecf20Sopenharmony_ci	.is_id = v2r0_is_id,
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic const struct qtree_fmt_operations v2r1_qtree_ops = {
398c2ecf20Sopenharmony_ci	.mem2disk_dqblk = v2r1_mem2diskdqb,
408c2ecf20Sopenharmony_ci	.disk2mem_dqblk = v2r1_disk2memdqb,
418c2ecf20Sopenharmony_ci	.is_id = v2r1_is_id,
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define QUOTABLOCK_BITS 10
458c2ecf20Sopenharmony_ci#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic inline qsize_t v2_stoqb(qsize_t space)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	return (space + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic inline qsize_t v2_qbtos(qsize_t blocks)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	return blocks << QUOTABLOCK_BITS;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int v2_read_header(struct super_block *sb, int type,
588c2ecf20Sopenharmony_ci			  struct v2_disk_dqheader *dqhead)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	ssize_t size;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	size = sb->s_op->quota_read(sb, type, (char *)dqhead,
638c2ecf20Sopenharmony_ci				    sizeof(struct v2_disk_dqheader), 0);
648c2ecf20Sopenharmony_ci	if (size != sizeof(struct v2_disk_dqheader)) {
658c2ecf20Sopenharmony_ci		quota_error(sb, "Failed header read: expected=%zd got=%zd",
668c2ecf20Sopenharmony_ci			    sizeof(struct v2_disk_dqheader), size);
678c2ecf20Sopenharmony_ci		if (size < 0)
688c2ecf20Sopenharmony_ci			return size;
698c2ecf20Sopenharmony_ci		return -EIO;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci	return 0;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* Check whether given file is really vfsv0 quotafile */
758c2ecf20Sopenharmony_cistatic int v2_check_quota_file(struct super_block *sb, int type)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct v2_disk_dqheader dqhead;
788c2ecf20Sopenharmony_ci	static const uint quota_magics[] = V2_INITQMAGICS;
798c2ecf20Sopenharmony_ci	static const uint quota_versions[] = V2_INITQVERSIONS;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (v2_read_header(sb, type, &dqhead))
828c2ecf20Sopenharmony_ci		return 0;
838c2ecf20Sopenharmony_ci	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
848c2ecf20Sopenharmony_ci	    le32_to_cpu(dqhead.dqh_version) > quota_versions[type])
858c2ecf20Sopenharmony_ci		return 0;
868c2ecf20Sopenharmony_ci	return 1;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* Read information header from quota file */
908c2ecf20Sopenharmony_cistatic int v2_read_file_info(struct super_block *sb, int type)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct v2_disk_dqinfo dinfo;
938c2ecf20Sopenharmony_ci	struct v2_disk_dqheader dqhead;
948c2ecf20Sopenharmony_ci	struct quota_info *dqopt = sb_dqopt(sb);
958c2ecf20Sopenharmony_ci	struct mem_dqinfo *info = &dqopt->info[type];
968c2ecf20Sopenharmony_ci	struct qtree_mem_dqinfo *qinfo;
978c2ecf20Sopenharmony_ci	ssize_t size;
988c2ecf20Sopenharmony_ci	unsigned int version;
998c2ecf20Sopenharmony_ci	int ret;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	down_read(&dqopt->dqio_sem);
1028c2ecf20Sopenharmony_ci	ret = v2_read_header(sb, type, &dqhead);
1038c2ecf20Sopenharmony_ci	if (ret < 0)
1048c2ecf20Sopenharmony_ci		goto out;
1058c2ecf20Sopenharmony_ci	version = le32_to_cpu(dqhead.dqh_version);
1068c2ecf20Sopenharmony_ci	if ((info->dqi_fmt_id == QFMT_VFS_V0 && version != 0) ||
1078c2ecf20Sopenharmony_ci	    (info->dqi_fmt_id == QFMT_VFS_V1 && version != 1)) {
1088c2ecf20Sopenharmony_ci		ret = -EINVAL;
1098c2ecf20Sopenharmony_ci		goto out;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
1138c2ecf20Sopenharmony_ci	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
1148c2ecf20Sopenharmony_ci	if (size != sizeof(struct v2_disk_dqinfo)) {
1158c2ecf20Sopenharmony_ci		quota_error(sb, "Can't read info structure");
1168c2ecf20Sopenharmony_ci		if (size < 0)
1178c2ecf20Sopenharmony_ci			ret = size;
1188c2ecf20Sopenharmony_ci		else
1198c2ecf20Sopenharmony_ci			ret = -EIO;
1208c2ecf20Sopenharmony_ci		goto out;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_NOFS);
1238c2ecf20Sopenharmony_ci	if (!info->dqi_priv) {
1248c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1258c2ecf20Sopenharmony_ci		goto out;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci	qinfo = info->dqi_priv;
1288c2ecf20Sopenharmony_ci	if (version == 0) {
1298c2ecf20Sopenharmony_ci		/* limits are stored as unsigned 32-bit data */
1308c2ecf20Sopenharmony_ci		info->dqi_max_spc_limit = 0xffffffffLL << QUOTABLOCK_BITS;
1318c2ecf20Sopenharmony_ci		info->dqi_max_ino_limit = 0xffffffff;
1328c2ecf20Sopenharmony_ci	} else {
1338c2ecf20Sopenharmony_ci		/*
1348c2ecf20Sopenharmony_ci		 * Used space is stored as unsigned 64-bit value in bytes but
1358c2ecf20Sopenharmony_ci		 * quota core supports only signed 64-bit values so use that
1368c2ecf20Sopenharmony_ci		 * as a limit
1378c2ecf20Sopenharmony_ci		 */
1388c2ecf20Sopenharmony_ci		info->dqi_max_spc_limit = 0x7fffffffffffffffLL; /* 2^63-1 */
1398c2ecf20Sopenharmony_ci		info->dqi_max_ino_limit = 0x7fffffffffffffffLL;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci	info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
1428c2ecf20Sopenharmony_ci	info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
1438c2ecf20Sopenharmony_ci	/* No flags currently supported */
1448c2ecf20Sopenharmony_ci	info->dqi_flags = 0;
1458c2ecf20Sopenharmony_ci	qinfo->dqi_sb = sb;
1468c2ecf20Sopenharmony_ci	qinfo->dqi_type = type;
1478c2ecf20Sopenharmony_ci	qinfo->dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
1488c2ecf20Sopenharmony_ci	qinfo->dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
1498c2ecf20Sopenharmony_ci	qinfo->dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
1508c2ecf20Sopenharmony_ci	qinfo->dqi_blocksize_bits = V2_DQBLKSIZE_BITS;
1518c2ecf20Sopenharmony_ci	qinfo->dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS;
1528c2ecf20Sopenharmony_ci	qinfo->dqi_qtree_depth = qtree_depth(qinfo);
1538c2ecf20Sopenharmony_ci	if (version == 0) {
1548c2ecf20Sopenharmony_ci		qinfo->dqi_entry_size = sizeof(struct v2r0_disk_dqblk);
1558c2ecf20Sopenharmony_ci		qinfo->dqi_ops = &v2r0_qtree_ops;
1568c2ecf20Sopenharmony_ci	} else {
1578c2ecf20Sopenharmony_ci		qinfo->dqi_entry_size = sizeof(struct v2r1_disk_dqblk);
1588c2ecf20Sopenharmony_ci		qinfo->dqi_ops = &v2r1_qtree_ops;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci	ret = -EUCLEAN;
1618c2ecf20Sopenharmony_ci	/* Some sanity checks of the read headers... */
1628c2ecf20Sopenharmony_ci	if ((loff_t)qinfo->dqi_blocks << qinfo->dqi_blocksize_bits >
1638c2ecf20Sopenharmony_ci	    i_size_read(sb_dqopt(sb)->files[type])) {
1648c2ecf20Sopenharmony_ci		quota_error(sb, "Number of blocks too big for quota file size (%llu > %llu).",
1658c2ecf20Sopenharmony_ci		    (loff_t)qinfo->dqi_blocks << qinfo->dqi_blocksize_bits,
1668c2ecf20Sopenharmony_ci		    i_size_read(sb_dqopt(sb)->files[type]));
1678c2ecf20Sopenharmony_ci		goto out_free;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	if (qinfo->dqi_free_blk >= qinfo->dqi_blocks) {
1708c2ecf20Sopenharmony_ci		quota_error(sb, "Free block number too big (%u >= %u).",
1718c2ecf20Sopenharmony_ci			    qinfo->dqi_free_blk, qinfo->dqi_blocks);
1728c2ecf20Sopenharmony_ci		goto out_free;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	if (qinfo->dqi_free_entry >= qinfo->dqi_blocks) {
1758c2ecf20Sopenharmony_ci		quota_error(sb, "Block with free entry too big (%u >= %u).",
1768c2ecf20Sopenharmony_ci			    qinfo->dqi_free_entry, qinfo->dqi_blocks);
1778c2ecf20Sopenharmony_ci		goto out_free;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci	ret = 0;
1808c2ecf20Sopenharmony_ciout_free:
1818c2ecf20Sopenharmony_ci	if (ret) {
1828c2ecf20Sopenharmony_ci		kfree(info->dqi_priv);
1838c2ecf20Sopenharmony_ci		info->dqi_priv = NULL;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ciout:
1868c2ecf20Sopenharmony_ci	up_read(&dqopt->dqio_sem);
1878c2ecf20Sopenharmony_ci	return ret;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/* Write information header to quota file */
1918c2ecf20Sopenharmony_cistatic int v2_write_file_info(struct super_block *sb, int type)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct v2_disk_dqinfo dinfo;
1948c2ecf20Sopenharmony_ci	struct quota_info *dqopt = sb_dqopt(sb);
1958c2ecf20Sopenharmony_ci	struct mem_dqinfo *info = &dqopt->info[type];
1968c2ecf20Sopenharmony_ci	struct qtree_mem_dqinfo *qinfo = info->dqi_priv;
1978c2ecf20Sopenharmony_ci	ssize_t size;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	down_write(&dqopt->dqio_sem);
2008c2ecf20Sopenharmony_ci	spin_lock(&dq_data_lock);
2018c2ecf20Sopenharmony_ci	info->dqi_flags &= ~DQF_INFO_DIRTY;
2028c2ecf20Sopenharmony_ci	dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
2038c2ecf20Sopenharmony_ci	dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
2048c2ecf20Sopenharmony_ci	/* No flags currently supported */
2058c2ecf20Sopenharmony_ci	dinfo.dqi_flags = cpu_to_le32(0);
2068c2ecf20Sopenharmony_ci	spin_unlock(&dq_data_lock);
2078c2ecf20Sopenharmony_ci	dinfo.dqi_blocks = cpu_to_le32(qinfo->dqi_blocks);
2088c2ecf20Sopenharmony_ci	dinfo.dqi_free_blk = cpu_to_le32(qinfo->dqi_free_blk);
2098c2ecf20Sopenharmony_ci	dinfo.dqi_free_entry = cpu_to_le32(qinfo->dqi_free_entry);
2108c2ecf20Sopenharmony_ci	size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
2118c2ecf20Sopenharmony_ci	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
2128c2ecf20Sopenharmony_ci	up_write(&dqopt->dqio_sem);
2138c2ecf20Sopenharmony_ci	if (size != sizeof(struct v2_disk_dqinfo)) {
2148c2ecf20Sopenharmony_ci		quota_error(sb, "Can't write info structure");
2158c2ecf20Sopenharmony_ci		return -1;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci	return 0;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic void v2r0_disk2memdqb(struct dquot *dquot, void *dp)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct v2r0_disk_dqblk *d = dp, empty;
2238c2ecf20Sopenharmony_ci	struct mem_dqblk *m = &dquot->dq_dqb;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);
2268c2ecf20Sopenharmony_ci	m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit);
2278c2ecf20Sopenharmony_ci	m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes);
2288c2ecf20Sopenharmony_ci	m->dqb_itime = le64_to_cpu(d->dqb_itime);
2298c2ecf20Sopenharmony_ci	m->dqb_bhardlimit = v2_qbtos(le32_to_cpu(d->dqb_bhardlimit));
2308c2ecf20Sopenharmony_ci	m->dqb_bsoftlimit = v2_qbtos(le32_to_cpu(d->dqb_bsoftlimit));
2318c2ecf20Sopenharmony_ci	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
2328c2ecf20Sopenharmony_ci	m->dqb_btime = le64_to_cpu(d->dqb_btime);
2338c2ecf20Sopenharmony_ci	/* We need to escape back all-zero structure */
2348c2ecf20Sopenharmony_ci	memset(&empty, 0, sizeof(struct v2r0_disk_dqblk));
2358c2ecf20Sopenharmony_ci	empty.dqb_itime = cpu_to_le64(1);
2368c2ecf20Sopenharmony_ci	if (!memcmp(&empty, dp, sizeof(struct v2r0_disk_dqblk)))
2378c2ecf20Sopenharmony_ci		m->dqb_itime = 0;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct v2r0_disk_dqblk *d = dp;
2438c2ecf20Sopenharmony_ci	struct mem_dqblk *m = &dquot->dq_dqb;
2448c2ecf20Sopenharmony_ci	struct qtree_mem_dqinfo *info =
2458c2ecf20Sopenharmony_ci			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
2488c2ecf20Sopenharmony_ci	d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
2498c2ecf20Sopenharmony_ci	d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);
2508c2ecf20Sopenharmony_ci	d->dqb_itime = cpu_to_le64(m->dqb_itime);
2518c2ecf20Sopenharmony_ci	d->dqb_bhardlimit = cpu_to_le32(v2_stoqb(m->dqb_bhardlimit));
2528c2ecf20Sopenharmony_ci	d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit));
2538c2ecf20Sopenharmony_ci	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
2548c2ecf20Sopenharmony_ci	d->dqb_btime = cpu_to_le64(m->dqb_btime);
2558c2ecf20Sopenharmony_ci	d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id));
2568c2ecf20Sopenharmony_ci	if (qtree_entry_unused(info, dp))
2578c2ecf20Sopenharmony_ci		d->dqb_itime = cpu_to_le64(1);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int v2r0_is_id(void *dp, struct dquot *dquot)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct v2r0_disk_dqblk *d = dp;
2638c2ecf20Sopenharmony_ci	struct qtree_mem_dqinfo *info =
2648c2ecf20Sopenharmony_ci			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (qtree_entry_unused(info, dp))
2678c2ecf20Sopenharmony_ci		return 0;
2688c2ecf20Sopenharmony_ci	return qid_eq(make_kqid(&init_user_ns, dquot->dq_id.type,
2698c2ecf20Sopenharmony_ci				le32_to_cpu(d->dqb_id)),
2708c2ecf20Sopenharmony_ci		      dquot->dq_id);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void v2r1_disk2memdqb(struct dquot *dquot, void *dp)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	struct v2r1_disk_dqblk *d = dp, empty;
2768c2ecf20Sopenharmony_ci	struct mem_dqblk *m = &dquot->dq_dqb;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
2798c2ecf20Sopenharmony_ci	m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
2808c2ecf20Sopenharmony_ci	m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
2818c2ecf20Sopenharmony_ci	m->dqb_itime = le64_to_cpu(d->dqb_itime);
2828c2ecf20Sopenharmony_ci	m->dqb_bhardlimit = v2_qbtos(le64_to_cpu(d->dqb_bhardlimit));
2838c2ecf20Sopenharmony_ci	m->dqb_bsoftlimit = v2_qbtos(le64_to_cpu(d->dqb_bsoftlimit));
2848c2ecf20Sopenharmony_ci	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
2858c2ecf20Sopenharmony_ci	m->dqb_btime = le64_to_cpu(d->dqb_btime);
2868c2ecf20Sopenharmony_ci	/* We need to escape back all-zero structure */
2878c2ecf20Sopenharmony_ci	memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
2888c2ecf20Sopenharmony_ci	empty.dqb_itime = cpu_to_le64(1);
2898c2ecf20Sopenharmony_ci	if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))
2908c2ecf20Sopenharmony_ci		m->dqb_itime = 0;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct v2r1_disk_dqblk *d = dp;
2968c2ecf20Sopenharmony_ci	struct mem_dqblk *m = &dquot->dq_dqb;
2978c2ecf20Sopenharmony_ci	struct qtree_mem_dqinfo *info =
2988c2ecf20Sopenharmony_ci			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
3018c2ecf20Sopenharmony_ci	d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
3028c2ecf20Sopenharmony_ci	d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
3038c2ecf20Sopenharmony_ci	d->dqb_itime = cpu_to_le64(m->dqb_itime);
3048c2ecf20Sopenharmony_ci	d->dqb_bhardlimit = cpu_to_le64(v2_stoqb(m->dqb_bhardlimit));
3058c2ecf20Sopenharmony_ci	d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit));
3068c2ecf20Sopenharmony_ci	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
3078c2ecf20Sopenharmony_ci	d->dqb_btime = cpu_to_le64(m->dqb_btime);
3088c2ecf20Sopenharmony_ci	d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id));
3098c2ecf20Sopenharmony_ci	d->dqb_pad = 0;
3108c2ecf20Sopenharmony_ci	if (qtree_entry_unused(info, dp))
3118c2ecf20Sopenharmony_ci		d->dqb_itime = cpu_to_le64(1);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic int v2r1_is_id(void *dp, struct dquot *dquot)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct v2r1_disk_dqblk *d = dp;
3178c2ecf20Sopenharmony_ci	struct qtree_mem_dqinfo *info =
3188c2ecf20Sopenharmony_ci			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (qtree_entry_unused(info, dp))
3218c2ecf20Sopenharmony_ci		return 0;
3228c2ecf20Sopenharmony_ci	return qid_eq(make_kqid(&init_user_ns, dquot->dq_id.type,
3238c2ecf20Sopenharmony_ci				le32_to_cpu(d->dqb_id)),
3248c2ecf20Sopenharmony_ci		      dquot->dq_id);
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic int v2_read_dquot(struct dquot *dquot)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
3308c2ecf20Sopenharmony_ci	int ret;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	down_read(&dqopt->dqio_sem);
3338c2ecf20Sopenharmony_ci	ret = qtree_read_dquot(
3348c2ecf20Sopenharmony_ci			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
3358c2ecf20Sopenharmony_ci			dquot);
3368c2ecf20Sopenharmony_ci	up_read(&dqopt->dqio_sem);
3378c2ecf20Sopenharmony_ci	return ret;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic int v2_write_dquot(struct dquot *dquot)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
3438c2ecf20Sopenharmony_ci	int ret;
3448c2ecf20Sopenharmony_ci	bool alloc = false;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/*
3478c2ecf20Sopenharmony_ci	 * If space for dquot is already allocated, we don't need any
3488c2ecf20Sopenharmony_ci	 * protection as we'll only overwrite the place of dquot. We are
3498c2ecf20Sopenharmony_ci	 * still protected by concurrent writes of the same dquot by
3508c2ecf20Sopenharmony_ci	 * dquot->dq_lock.
3518c2ecf20Sopenharmony_ci	 */
3528c2ecf20Sopenharmony_ci	if (!dquot->dq_off) {
3538c2ecf20Sopenharmony_ci		alloc = true;
3548c2ecf20Sopenharmony_ci		down_write(&dqopt->dqio_sem);
3558c2ecf20Sopenharmony_ci	} else {
3568c2ecf20Sopenharmony_ci		down_read(&dqopt->dqio_sem);
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci	ret = qtree_write_dquot(
3598c2ecf20Sopenharmony_ci			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
3608c2ecf20Sopenharmony_ci			dquot);
3618c2ecf20Sopenharmony_ci	if (alloc)
3628c2ecf20Sopenharmony_ci		up_write(&dqopt->dqio_sem);
3638c2ecf20Sopenharmony_ci	else
3648c2ecf20Sopenharmony_ci		up_read(&dqopt->dqio_sem);
3658c2ecf20Sopenharmony_ci	return ret;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic int v2_release_dquot(struct dquot *dquot)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
3718c2ecf20Sopenharmony_ci	int ret;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	down_write(&dqopt->dqio_sem);
3748c2ecf20Sopenharmony_ci	ret = qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
3758c2ecf20Sopenharmony_ci	up_write(&dqopt->dqio_sem);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return ret;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int v2_free_file_info(struct super_block *sb, int type)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	kfree(sb_dqinfo(sb, type)->dqi_priv);
3838c2ecf20Sopenharmony_ci	return 0;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic int v2_get_next_id(struct super_block *sb, struct kqid *qid)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	struct quota_info *dqopt = sb_dqopt(sb);
3898c2ecf20Sopenharmony_ci	int ret;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	down_read(&dqopt->dqio_sem);
3928c2ecf20Sopenharmony_ci	ret = qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid);
3938c2ecf20Sopenharmony_ci	up_read(&dqopt->dqio_sem);
3948c2ecf20Sopenharmony_ci	return ret;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic const struct quota_format_ops v2_format_ops = {
3988c2ecf20Sopenharmony_ci	.check_quota_file	= v2_check_quota_file,
3998c2ecf20Sopenharmony_ci	.read_file_info		= v2_read_file_info,
4008c2ecf20Sopenharmony_ci	.write_file_info	= v2_write_file_info,
4018c2ecf20Sopenharmony_ci	.free_file_info		= v2_free_file_info,
4028c2ecf20Sopenharmony_ci	.read_dqblk		= v2_read_dquot,
4038c2ecf20Sopenharmony_ci	.commit_dqblk		= v2_write_dquot,
4048c2ecf20Sopenharmony_ci	.release_dqblk		= v2_release_dquot,
4058c2ecf20Sopenharmony_ci	.get_next_id		= v2_get_next_id,
4068c2ecf20Sopenharmony_ci};
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic struct quota_format_type v2r0_quota_format = {
4098c2ecf20Sopenharmony_ci	.qf_fmt_id	= QFMT_VFS_V0,
4108c2ecf20Sopenharmony_ci	.qf_ops		= &v2_format_ops,
4118c2ecf20Sopenharmony_ci	.qf_owner	= THIS_MODULE
4128c2ecf20Sopenharmony_ci};
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic struct quota_format_type v2r1_quota_format = {
4158c2ecf20Sopenharmony_ci	.qf_fmt_id	= QFMT_VFS_V1,
4168c2ecf20Sopenharmony_ci	.qf_ops		= &v2_format_ops,
4178c2ecf20Sopenharmony_ci	.qf_owner	= THIS_MODULE
4188c2ecf20Sopenharmony_ci};
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int __init init_v2_quota_format(void)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	int ret;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	ret = register_quota_format(&v2r0_quota_format);
4258c2ecf20Sopenharmony_ci	if (ret)
4268c2ecf20Sopenharmony_ci		return ret;
4278c2ecf20Sopenharmony_ci	return register_quota_format(&v2r1_quota_format);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic void __exit exit_v2_quota_format(void)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	unregister_quota_format(&v2r0_quota_format);
4338c2ecf20Sopenharmony_ci	unregister_quota_format(&v2r1_quota_format);
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cimodule_init(init_v2_quota_format);
4378c2ecf20Sopenharmony_cimodule_exit(exit_v2_quota_format);
438