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