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