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