brintos

brintos / linux-shallow public Read only

0
0
Text · 13.3 KiB · 1fda93d Raw
456 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *	vfsv0 quota IO operations on file4 */5 6#include <linux/errno.h>7#include <linux/fs.h>8#include <linux/mount.h>9#include <linux/dqblk_v2.h>10#include <linux/kernel.h>11#include <linux/init.h>12#include <linux/module.h>13#include <linux/slab.h>14#include <linux/quotaops.h>15 16#include <asm/byteorder.h>17 18#include "quota_tree.h"19#include "quotaio_v2.h"20 21MODULE_AUTHOR("Jan Kara");22MODULE_DESCRIPTION("Quota format v2 support");23MODULE_LICENSE("GPL");24 25static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot);26static void v2r0_disk2memdqb(struct dquot *dquot, void *dp);27static int v2r0_is_id(void *dp, struct dquot *dquot);28static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot);29static void v2r1_disk2memdqb(struct dquot *dquot, void *dp);30static int v2r1_is_id(void *dp, struct dquot *dquot);31 32static const struct qtree_fmt_operations v2r0_qtree_ops = {33	.mem2disk_dqblk = v2r0_mem2diskdqb,34	.disk2mem_dqblk = v2r0_disk2memdqb,35	.is_id = v2r0_is_id,36};37 38static const struct qtree_fmt_operations v2r1_qtree_ops = {39	.mem2disk_dqblk = v2r1_mem2diskdqb,40	.disk2mem_dqblk = v2r1_disk2memdqb,41	.is_id = v2r1_is_id,42};43 44#define QUOTABLOCK_BITS 1045#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)46 47static inline qsize_t v2_stoqb(qsize_t space)48{49	return (space + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS;50}51 52static inline qsize_t v2_qbtos(qsize_t blocks)53{54	return blocks << QUOTABLOCK_BITS;55}56 57static int v2_read_header(struct super_block *sb, int type,58			  struct v2_disk_dqheader *dqhead)59{60	ssize_t size;61 62	size = sb->s_op->quota_read(sb, type, (char *)dqhead,63				    sizeof(struct v2_disk_dqheader), 0);64	if (size != sizeof(struct v2_disk_dqheader)) {65		quota_error(sb, "Failed header read: expected=%zd got=%zd",66			    sizeof(struct v2_disk_dqheader), size);67		if (size < 0)68			return size;69		return -EIO;70	}71	return 0;72}73 74/* Check whether given file is really vfsv0 quotafile */75static int v2_check_quota_file(struct super_block *sb, int type)76{77	struct v2_disk_dqheader dqhead;78	static const uint quota_magics[] = V2_INITQMAGICS;79	static const uint quota_versions[] = V2_INITQVERSIONS;80 81	if (v2_read_header(sb, type, &dqhead))82		return 0;83	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||84	    le32_to_cpu(dqhead.dqh_version) > quota_versions[type])85		return 0;86	return 1;87}88 89/* Read information header from quota file */90static int v2_read_file_info(struct super_block *sb, int type)91{92	struct v2_disk_dqinfo dinfo;93	struct v2_disk_dqheader dqhead;94	struct quota_info *dqopt = sb_dqopt(sb);95	struct mem_dqinfo *info = &dqopt->info[type];96	struct qtree_mem_dqinfo *qinfo;97	ssize_t size;98	unsigned int version;99	unsigned int memalloc;100	int ret;101 102	down_read(&dqopt->dqio_sem);103	memalloc = memalloc_nofs_save();104	ret = v2_read_header(sb, type, &dqhead);105	if (ret < 0)106		goto out;107	version = le32_to_cpu(dqhead.dqh_version);108	if ((info->dqi_fmt_id == QFMT_VFS_V0 && version != 0) ||109	    (info->dqi_fmt_id == QFMT_VFS_V1 && version != 1)) {110		ret = -EINVAL;111		goto out;112	}113 114	size = sb->s_op->quota_read(sb, type, (char *)&dinfo,115	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);116	if (size != sizeof(struct v2_disk_dqinfo)) {117		quota_error(sb, "Can't read info structure");118		if (size < 0)119			ret = size;120		else121			ret = -EIO;122		goto out;123	}124	info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_KERNEL);125	if (!info->dqi_priv) {126		ret = -ENOMEM;127		goto out;128	}129	qinfo = info->dqi_priv;130	if (version == 0) {131		/* limits are stored as unsigned 32-bit data */132		info->dqi_max_spc_limit = 0xffffffffLL << QUOTABLOCK_BITS;133		info->dqi_max_ino_limit = 0xffffffff;134	} else {135		/*136		 * Used space is stored as unsigned 64-bit value in bytes but137		 * quota core supports only signed 64-bit values so use that138		 * as a limit139		 */140		info->dqi_max_spc_limit = 0x7fffffffffffffffLL; /* 2^63-1 */141		info->dqi_max_ino_limit = 0x7fffffffffffffffLL;142	}143	info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);144	info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);145	/* No flags currently supported */146	info->dqi_flags = 0;147	qinfo->dqi_sb = sb;148	qinfo->dqi_type = type;149	qinfo->dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);150	qinfo->dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);151	qinfo->dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);152	qinfo->dqi_blocksize_bits = V2_DQBLKSIZE_BITS;153	qinfo->dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS;154	qinfo->dqi_qtree_depth = qtree_depth(qinfo);155	if (version == 0) {156		qinfo->dqi_entry_size = sizeof(struct v2r0_disk_dqblk);157		qinfo->dqi_ops = &v2r0_qtree_ops;158	} else {159		qinfo->dqi_entry_size = sizeof(struct v2r1_disk_dqblk);160		qinfo->dqi_ops = &v2r1_qtree_ops;161	}162	ret = -EUCLEAN;163	/* Some sanity checks of the read headers... */164	if ((loff_t)qinfo->dqi_blocks << qinfo->dqi_blocksize_bits >165	    i_size_read(sb_dqopt(sb)->files[type])) {166		quota_error(sb, "Number of blocks too big for quota file size (%llu > %llu).",167		    (loff_t)qinfo->dqi_blocks << qinfo->dqi_blocksize_bits,168		    i_size_read(sb_dqopt(sb)->files[type]));169		goto out_free;170	}171	if (qinfo->dqi_free_blk && (qinfo->dqi_free_blk <= QT_TREEOFF ||172	    qinfo->dqi_free_blk >= qinfo->dqi_blocks)) {173		quota_error(sb, "Free block number %u out of range (%u, %u).",174			    qinfo->dqi_free_blk, QT_TREEOFF, qinfo->dqi_blocks);175		goto out_free;176	}177	if (qinfo->dqi_free_entry && (qinfo->dqi_free_entry <= QT_TREEOFF ||178	    qinfo->dqi_free_entry >= qinfo->dqi_blocks)) {179		quota_error(sb, "Block with free entry %u out of range (%u, %u).",180			    qinfo->dqi_free_entry, QT_TREEOFF,181			    qinfo->dqi_blocks);182		goto out_free;183	}184	ret = 0;185out_free:186	if (ret) {187		kfree(info->dqi_priv);188		info->dqi_priv = NULL;189	}190out:191	memalloc_nofs_restore(memalloc);192	up_read(&dqopt->dqio_sem);193	return ret;194}195 196/* Write information header to quota file */197static int v2_write_file_info(struct super_block *sb, int type)198{199	struct v2_disk_dqinfo dinfo;200	struct quota_info *dqopt = sb_dqopt(sb);201	struct mem_dqinfo *info = &dqopt->info[type];202	struct qtree_mem_dqinfo *qinfo = info->dqi_priv;203	ssize_t size;204	unsigned int memalloc;205 206	down_write(&dqopt->dqio_sem);207	memalloc = memalloc_nofs_save();208	spin_lock(&dq_data_lock);209	info->dqi_flags &= ~DQF_INFO_DIRTY;210	dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);211	dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);212	/* No flags currently supported */213	dinfo.dqi_flags = cpu_to_le32(0);214	spin_unlock(&dq_data_lock);215	dinfo.dqi_blocks = cpu_to_le32(qinfo->dqi_blocks);216	dinfo.dqi_free_blk = cpu_to_le32(qinfo->dqi_free_blk);217	dinfo.dqi_free_entry = cpu_to_le32(qinfo->dqi_free_entry);218	size = sb->s_op->quota_write(sb, type, (char *)&dinfo,219	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);220	memalloc_nofs_restore(memalloc);221	up_write(&dqopt->dqio_sem);222	if (size != sizeof(struct v2_disk_dqinfo)) {223		quota_error(sb, "Can't write info structure");224		return size < 0 ? size : -EIO;225	}226	return 0;227}228 229static void v2r0_disk2memdqb(struct dquot *dquot, void *dp)230{231	struct v2r0_disk_dqblk *d = dp, empty;232	struct mem_dqblk *m = &dquot->dq_dqb;233 234	m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);235	m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit);236	m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes);237	m->dqb_itime = le64_to_cpu(d->dqb_itime);238	m->dqb_bhardlimit = v2_qbtos(le32_to_cpu(d->dqb_bhardlimit));239	m->dqb_bsoftlimit = v2_qbtos(le32_to_cpu(d->dqb_bsoftlimit));240	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);241	m->dqb_btime = le64_to_cpu(d->dqb_btime);242	/* We need to escape back all-zero structure */243	memset(&empty, 0, sizeof(struct v2r0_disk_dqblk));244	empty.dqb_itime = cpu_to_le64(1);245	if (!memcmp(&empty, dp, sizeof(struct v2r0_disk_dqblk)))246		m->dqb_itime = 0;247}248 249static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)250{251	struct v2r0_disk_dqblk *d = dp;252	struct mem_dqblk *m = &dquot->dq_dqb;253	struct qtree_mem_dqinfo *info =254			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;255 256	d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);257	d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);258	d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);259	d->dqb_itime = cpu_to_le64(m->dqb_itime);260	d->dqb_bhardlimit = cpu_to_le32(v2_stoqb(m->dqb_bhardlimit));261	d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit));262	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);263	d->dqb_btime = cpu_to_le64(m->dqb_btime);264	d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id));265	if (qtree_entry_unused(info, dp))266		d->dqb_itime = cpu_to_le64(1);267}268 269static int v2r0_is_id(void *dp, struct dquot *dquot)270{271	struct v2r0_disk_dqblk *d = dp;272	struct qtree_mem_dqinfo *info =273			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;274 275	if (qtree_entry_unused(info, dp))276		return 0;277	return qid_eq(make_kqid(&init_user_ns, dquot->dq_id.type,278				le32_to_cpu(d->dqb_id)),279		      dquot->dq_id);280}281 282static void v2r1_disk2memdqb(struct dquot *dquot, void *dp)283{284	struct v2r1_disk_dqblk *d = dp, empty;285	struct mem_dqblk *m = &dquot->dq_dqb;286 287	m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);288	m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);289	m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);290	m->dqb_itime = le64_to_cpu(d->dqb_itime);291	m->dqb_bhardlimit = v2_qbtos(le64_to_cpu(d->dqb_bhardlimit));292	m->dqb_bsoftlimit = v2_qbtos(le64_to_cpu(d->dqb_bsoftlimit));293	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);294	m->dqb_btime = le64_to_cpu(d->dqb_btime);295	/* We need to escape back all-zero structure */296	memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));297	empty.dqb_itime = cpu_to_le64(1);298	if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))299		m->dqb_itime = 0;300}301 302static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)303{304	struct v2r1_disk_dqblk *d = dp;305	struct mem_dqblk *m = &dquot->dq_dqb;306	struct qtree_mem_dqinfo *info =307			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;308 309	d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);310	d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);311	d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);312	d->dqb_itime = cpu_to_le64(m->dqb_itime);313	d->dqb_bhardlimit = cpu_to_le64(v2_stoqb(m->dqb_bhardlimit));314	d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit));315	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);316	d->dqb_btime = cpu_to_le64(m->dqb_btime);317	d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id));318	d->dqb_pad = 0;319	if (qtree_entry_unused(info, dp))320		d->dqb_itime = cpu_to_le64(1);321}322 323static int v2r1_is_id(void *dp, struct dquot *dquot)324{325	struct v2r1_disk_dqblk *d = dp;326	struct qtree_mem_dqinfo *info =327			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;328 329	if (qtree_entry_unused(info, dp))330		return 0;331	return qid_eq(make_kqid(&init_user_ns, dquot->dq_id.type,332				le32_to_cpu(d->dqb_id)),333		      dquot->dq_id);334}335 336static int v2_read_dquot(struct dquot *dquot)337{338	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);339	int ret;340	unsigned int memalloc;341 342	down_read(&dqopt->dqio_sem);343	memalloc = memalloc_nofs_save();344	ret = qtree_read_dquot(345			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,346			dquot);347	memalloc_nofs_restore(memalloc);348	up_read(&dqopt->dqio_sem);349	return ret;350}351 352static int v2_write_dquot(struct dquot *dquot)353{354	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);355	int ret;356	bool alloc = false;357	unsigned int memalloc;358 359	/*360	 * If space for dquot is already allocated, we don't need any361	 * protection as we'll only overwrite the place of dquot. We are362	 * still protected by concurrent writes of the same dquot by363	 * dquot->dq_lock.364	 */365	if (!dquot->dq_off) {366		alloc = true;367		down_write(&dqopt->dqio_sem);368	} else {369		down_read(&dqopt->dqio_sem);370	}371	memalloc = memalloc_nofs_save();372	ret = qtree_write_dquot(373			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,374			dquot);375	memalloc_nofs_restore(memalloc);376	if (alloc)377		up_write(&dqopt->dqio_sem);378	else379		up_read(&dqopt->dqio_sem);380	return ret;381}382 383static int v2_release_dquot(struct dquot *dquot)384{385	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);386	unsigned int memalloc;387	int ret;388 389	down_write(&dqopt->dqio_sem);390	memalloc = memalloc_nofs_save();391	ret = qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);392	memalloc_nofs_restore(memalloc);393	up_write(&dqopt->dqio_sem);394 395	return ret;396}397 398static int v2_free_file_info(struct super_block *sb, int type)399{400	kfree(sb_dqinfo(sb, type)->dqi_priv);401	return 0;402}403 404static int v2_get_next_id(struct super_block *sb, struct kqid *qid)405{406	struct quota_info *dqopt = sb_dqopt(sb);407	unsigned int memalloc;408	int ret;409 410	down_read(&dqopt->dqio_sem);411	memalloc = memalloc_nofs_save();412	ret = qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid);413	memalloc_nofs_restore(memalloc);414	up_read(&dqopt->dqio_sem);415	return ret;416}417 418static const struct quota_format_ops v2_format_ops = {419	.check_quota_file	= v2_check_quota_file,420	.read_file_info		= v2_read_file_info,421	.write_file_info	= v2_write_file_info,422	.free_file_info		= v2_free_file_info,423	.read_dqblk		= v2_read_dquot,424	.commit_dqblk		= v2_write_dquot,425	.release_dqblk		= v2_release_dquot,426	.get_next_id		= v2_get_next_id,427};428 429static struct quota_format_type v2r0_quota_format = {430	.qf_fmt_id	= QFMT_VFS_V0,431	.qf_ops		= &v2_format_ops,432	.qf_owner	= THIS_MODULE433};434 435static struct quota_format_type v2r1_quota_format = {436	.qf_fmt_id	= QFMT_VFS_V1,437	.qf_ops		= &v2_format_ops,438	.qf_owner	= THIS_MODULE439};440 441static int __init init_v2_quota_format(void)442{443	register_quota_format(&v2r0_quota_format);444	register_quota_format(&v2r1_quota_format);445	return 0;446}447 448static void __exit exit_v2_quota_format(void)449{450	unregister_quota_format(&v2r0_quota_format);451	unregister_quota_format(&v2r1_quota_format);452}453 454module_init(init_v2_quota_format);455module_exit(exit_v2_quota_format);456