cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

quota_v1.c (6794B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2#include <linux/errno.h>
      3#include <linux/fs.h>
      4#include <linux/quota.h>
      5#include <linux/quotaops.h>
      6#include <linux/dqblk_v1.h>
      7#include <linux/kernel.h>
      8#include <linux/init.h>
      9#include <linux/module.h>
     10
     11#include <asm/byteorder.h>
     12
     13#include "quotaio_v1.h"
     14
     15MODULE_AUTHOR("Jan Kara");
     16MODULE_DESCRIPTION("Old quota format support");
     17MODULE_LICENSE("GPL");
     18
     19#define QUOTABLOCK_BITS 10
     20#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
     21
     22static inline qsize_t v1_stoqb(qsize_t space)
     23{
     24	return (space + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS;
     25}
     26
     27static inline qsize_t v1_qbtos(qsize_t blocks)
     28{
     29	return blocks << QUOTABLOCK_BITS;
     30}
     31
     32static void v1_disk2mem_dqblk(struct mem_dqblk *m, struct v1_disk_dqblk *d)
     33{
     34	m->dqb_ihardlimit = d->dqb_ihardlimit;
     35	m->dqb_isoftlimit = d->dqb_isoftlimit;
     36	m->dqb_curinodes = d->dqb_curinodes;
     37	m->dqb_bhardlimit = v1_qbtos(d->dqb_bhardlimit);
     38	m->dqb_bsoftlimit = v1_qbtos(d->dqb_bsoftlimit);
     39	m->dqb_curspace = v1_qbtos(d->dqb_curblocks);
     40	m->dqb_itime = d->dqb_itime;
     41	m->dqb_btime = d->dqb_btime;
     42}
     43
     44static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)
     45{
     46	d->dqb_ihardlimit = m->dqb_ihardlimit;
     47	d->dqb_isoftlimit = m->dqb_isoftlimit;
     48	d->dqb_curinodes = m->dqb_curinodes;
     49	d->dqb_bhardlimit = v1_stoqb(m->dqb_bhardlimit);
     50	d->dqb_bsoftlimit = v1_stoqb(m->dqb_bsoftlimit);
     51	d->dqb_curblocks = v1_stoqb(m->dqb_curspace);
     52	d->dqb_itime = m->dqb_itime;
     53	d->dqb_btime = m->dqb_btime;
     54}
     55
     56static int v1_read_dqblk(struct dquot *dquot)
     57{
     58	int type = dquot->dq_id.type;
     59	struct v1_disk_dqblk dqblk;
     60	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
     61
     62	if (!dqopt->files[type])
     63		return -EINVAL;
     64
     65	/* Set structure to 0s in case read fails/is after end of file */
     66	memset(&dqblk, 0, sizeof(struct v1_disk_dqblk));
     67	dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk,
     68			sizeof(struct v1_disk_dqblk),
     69			v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id)));
     70
     71	v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
     72	if (dquot->dq_dqb.dqb_bhardlimit == 0 &&
     73	    dquot->dq_dqb.dqb_bsoftlimit == 0 &&
     74	    dquot->dq_dqb.dqb_ihardlimit == 0 &&
     75	    dquot->dq_dqb.dqb_isoftlimit == 0)
     76		set_bit(DQ_FAKE_B, &dquot->dq_flags);
     77	dqstats_inc(DQST_READS);
     78
     79	return 0;
     80}
     81
     82static int v1_commit_dqblk(struct dquot *dquot)
     83{
     84	short type = dquot->dq_id.type;
     85	ssize_t ret;
     86	struct v1_disk_dqblk dqblk;
     87
     88	v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
     89	if (((type == USRQUOTA) && uid_eq(dquot->dq_id.uid, GLOBAL_ROOT_UID)) ||
     90	    ((type == GRPQUOTA) && gid_eq(dquot->dq_id.gid, GLOBAL_ROOT_GID))) {
     91		dqblk.dqb_btime =
     92			sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
     93		dqblk.dqb_itime =
     94			sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace;
     95	}
     96	ret = 0;
     97	if (sb_dqopt(dquot->dq_sb)->files[type])
     98		ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
     99			(char *)&dqblk, sizeof(struct v1_disk_dqblk),
    100			v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id)));
    101	if (ret != sizeof(struct v1_disk_dqblk)) {
    102		quota_error(dquot->dq_sb, "dquota write failed");
    103		if (ret >= 0)
    104			ret = -EIO;
    105		goto out;
    106	}
    107	ret = 0;
    108
    109out:
    110	dqstats_inc(DQST_WRITES);
    111
    112	return ret;
    113}
    114
    115/* Magics of new quota format */
    116#define V2_INITQMAGICS {\
    117	0xd9c01f11,     /* USRQUOTA */\
    118	0xd9c01927      /* GRPQUOTA */\
    119}
    120
    121/* Header of new quota format */
    122struct v2_disk_dqheader {
    123	__le32 dqh_magic;        /* Magic number identifying file */
    124	__le32 dqh_version;      /* File version */
    125};
    126
    127static int v1_check_quota_file(struct super_block *sb, int type)
    128{
    129	struct inode *inode = sb_dqopt(sb)->files[type];
    130	ulong blocks;
    131	size_t off;
    132	struct v2_disk_dqheader dqhead;
    133	ssize_t size;
    134	loff_t isize;
    135	static const uint quota_magics[] = V2_INITQMAGICS;
    136
    137	isize = i_size_read(inode);
    138	if (!isize)
    139		return 0;
    140	blocks = isize >> BLOCK_SIZE_BITS;
    141	off = isize & (BLOCK_SIZE - 1);
    142	if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) %
    143	    sizeof(struct v1_disk_dqblk))
    144		return 0;
    145	/* Doublecheck whether we didn't get file with new format - with old
    146	 * quotactl() this could happen */
    147	size = sb->s_op->quota_read(sb, type, (char *)&dqhead,
    148				    sizeof(struct v2_disk_dqheader), 0);
    149	if (size != sizeof(struct v2_disk_dqheader))
    150		return 1;	/* Probably not new format */
    151	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
    152		return 1;	/* Definitely not new format */
    153	printk(KERN_INFO
    154	       "VFS: %s: Refusing to turn on old quota format on given file."
    155	       " It probably contains newer quota format.\n", sb->s_id);
    156        return 0;		/* Seems like a new format file -> refuse it */
    157}
    158
    159static int v1_read_file_info(struct super_block *sb, int type)
    160{
    161	struct quota_info *dqopt = sb_dqopt(sb);
    162	struct v1_disk_dqblk dqblk;
    163	int ret;
    164
    165	down_read(&dqopt->dqio_sem);
    166	ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
    167				sizeof(struct v1_disk_dqblk), v1_dqoff(0));
    168	if (ret != sizeof(struct v1_disk_dqblk)) {
    169		if (ret >= 0)
    170			ret = -EIO;
    171		goto out;
    172	}
    173	ret = 0;
    174	/* limits are stored as unsigned 32-bit data */
    175	dqopt->info[type].dqi_max_spc_limit = 0xffffffffULL << QUOTABLOCK_BITS;
    176	dqopt->info[type].dqi_max_ino_limit = 0xffffffff;
    177	dqopt->info[type].dqi_igrace =
    178			dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME;
    179	dqopt->info[type].dqi_bgrace =
    180			dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
    181out:
    182	up_read(&dqopt->dqio_sem);
    183	return ret;
    184}
    185
    186static int v1_write_file_info(struct super_block *sb, int type)
    187{
    188	struct quota_info *dqopt = sb_dqopt(sb);
    189	struct v1_disk_dqblk dqblk;
    190	int ret;
    191
    192	down_write(&dqopt->dqio_sem);
    193	ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
    194				sizeof(struct v1_disk_dqblk), v1_dqoff(0));
    195	if (ret != sizeof(struct v1_disk_dqblk)) {
    196		if (ret >= 0)
    197			ret = -EIO;
    198		goto out;
    199	}
    200	spin_lock(&dq_data_lock);
    201	dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY;
    202	dqblk.dqb_itime = dqopt->info[type].dqi_igrace;
    203	dqblk.dqb_btime = dqopt->info[type].dqi_bgrace;
    204	spin_unlock(&dq_data_lock);
    205	ret = sb->s_op->quota_write(sb, type, (char *)&dqblk,
    206	      sizeof(struct v1_disk_dqblk), v1_dqoff(0));
    207	if (ret == sizeof(struct v1_disk_dqblk))
    208		ret = 0;
    209	else if (ret > 0)
    210		ret = -EIO;
    211out:
    212	up_write(&dqopt->dqio_sem);
    213	return ret;
    214}
    215
    216static const struct quota_format_ops v1_format_ops = {
    217	.check_quota_file	= v1_check_quota_file,
    218	.read_file_info		= v1_read_file_info,
    219	.write_file_info	= v1_write_file_info,
    220	.read_dqblk		= v1_read_dqblk,
    221	.commit_dqblk		= v1_commit_dqblk,
    222};
    223
    224static struct quota_format_type v1_quota_format = {
    225	.qf_fmt_id	= QFMT_VFS_OLD,
    226	.qf_ops		= &v1_format_ops,
    227	.qf_owner	= THIS_MODULE
    228};
    229
    230static int __init init_v1_quota_format(void)
    231{
    232        return register_quota_format(&v1_quota_format);
    233}
    234
    235static void __exit exit_v1_quota_format(void)
    236{
    237        unregister_quota_format(&v1_quota_format);
    238}
    239
    240module_init(init_v1_quota_format);
    241module_exit(exit_v1_quota_format);
    242