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 | static inline qsize_t v2_stoqb(qsize_t space) |
48 | { |
49 | return (space + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS; |
50 | } |
51 | |
52 | static inline qsize_t v2_qbtos(qsize_t blocks) |
53 | { |
54 | return blocks << QUOTABLOCK_BITS; |
55 | } |
56 | |
57 | static int (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 */ |
75 | static 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: &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 */ |
90 | static 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 | int ret; |
100 | |
101 | down_read(sem: &dqopt->dqio_sem); |
102 | ret = v2_read_header(sb, type, dqhead: &dqhead); |
103 | if (ret < 0) |
104 | goto out; |
105 | version = le32_to_cpu(dqhead.dqh_version); |
106 | if ((info->dqi_fmt_id == QFMT_VFS_V0 && version != 0) || |
107 | (info->dqi_fmt_id == QFMT_VFS_V1 && version != 1)) { |
108 | ret = -EINVAL; |
109 | goto out; |
110 | } |
111 | |
112 | size = sb->s_op->quota_read(sb, type, (char *)&dinfo, |
113 | sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); |
114 | if (size != sizeof(struct v2_disk_dqinfo)) { |
115 | quota_error(sb, "Can't read info structure" ); |
116 | if (size < 0) |
117 | ret = size; |
118 | else |
119 | ret = -EIO; |
120 | goto out; |
121 | } |
122 | info->dqi_priv = kmalloc(size: sizeof(struct qtree_mem_dqinfo), GFP_NOFS); |
123 | if (!info->dqi_priv) { |
124 | ret = -ENOMEM; |
125 | goto out; |
126 | } |
127 | qinfo = info->dqi_priv; |
128 | if (version == 0) { |
129 | /* limits are stored as unsigned 32-bit data */ |
130 | info->dqi_max_spc_limit = 0xffffffffLL << QUOTABLOCK_BITS; |
131 | 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 | info->dqi_max_spc_limit = 0x7fffffffffffffffLL; /* 2^63-1 */ |
139 | info->dqi_max_ino_limit = 0x7fffffffffffffffLL; |
140 | } |
141 | info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); |
142 | info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace); |
143 | /* No flags currently supported */ |
144 | info->dqi_flags = 0; |
145 | qinfo->dqi_sb = sb; |
146 | qinfo->dqi_type = type; |
147 | qinfo->dqi_blocks = le32_to_cpu(dinfo.dqi_blocks); |
148 | qinfo->dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk); |
149 | qinfo->dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry); |
150 | qinfo->dqi_blocksize_bits = V2_DQBLKSIZE_BITS; |
151 | qinfo->dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS; |
152 | qinfo->dqi_qtree_depth = qtree_depth(info: qinfo); |
153 | if (version == 0) { |
154 | qinfo->dqi_entry_size = sizeof(struct v2r0_disk_dqblk); |
155 | qinfo->dqi_ops = &v2r0_qtree_ops; |
156 | } else { |
157 | qinfo->dqi_entry_size = sizeof(struct v2r1_disk_dqblk); |
158 | qinfo->dqi_ops = &v2r1_qtree_ops; |
159 | } |
160 | ret = -EUCLEAN; |
161 | /* Some sanity checks of the read headers... */ |
162 | if ((loff_t)qinfo->dqi_blocks << qinfo->dqi_blocksize_bits > |
163 | i_size_read(inode: sb_dqopt(sb)->files[type])) { |
164 | quota_error(sb, "Number of blocks too big for quota file size (%llu > %llu)." , |
165 | (loff_t)qinfo->dqi_blocks << qinfo->dqi_blocksize_bits, |
166 | i_size_read(sb_dqopt(sb)->files[type])); |
167 | goto out_free; |
168 | } |
169 | if (qinfo->dqi_free_blk >= qinfo->dqi_blocks) { |
170 | quota_error(sb, "Free block number too big (%u >= %u)." , |
171 | qinfo->dqi_free_blk, qinfo->dqi_blocks); |
172 | goto out_free; |
173 | } |
174 | if (qinfo->dqi_free_entry >= qinfo->dqi_blocks) { |
175 | quota_error(sb, "Block with free entry too big (%u >= %u)." , |
176 | qinfo->dqi_free_entry, qinfo->dqi_blocks); |
177 | goto out_free; |
178 | } |
179 | ret = 0; |
180 | out_free: |
181 | if (ret) { |
182 | kfree(objp: info->dqi_priv); |
183 | info->dqi_priv = NULL; |
184 | } |
185 | out: |
186 | up_read(sem: &dqopt->dqio_sem); |
187 | return ret; |
188 | } |
189 | |
190 | /* Write information header to quota file */ |
191 | static int v2_write_file_info(struct super_block *sb, int type) |
192 | { |
193 | struct v2_disk_dqinfo dinfo; |
194 | struct quota_info *dqopt = sb_dqopt(sb); |
195 | struct mem_dqinfo *info = &dqopt->info[type]; |
196 | struct qtree_mem_dqinfo *qinfo = info->dqi_priv; |
197 | ssize_t size; |
198 | |
199 | down_write(sem: &dqopt->dqio_sem); |
200 | spin_lock(lock: &dq_data_lock); |
201 | info->dqi_flags &= ~DQF_INFO_DIRTY; |
202 | dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace); |
203 | dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace); |
204 | /* No flags currently supported */ |
205 | dinfo.dqi_flags = cpu_to_le32(0); |
206 | spin_unlock(lock: &dq_data_lock); |
207 | dinfo.dqi_blocks = cpu_to_le32(qinfo->dqi_blocks); |
208 | dinfo.dqi_free_blk = cpu_to_le32(qinfo->dqi_free_blk); |
209 | dinfo.dqi_free_entry = cpu_to_le32(qinfo->dqi_free_entry); |
210 | size = sb->s_op->quota_write(sb, type, (char *)&dinfo, |
211 | sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); |
212 | up_write(sem: &dqopt->dqio_sem); |
213 | if (size != sizeof(struct v2_disk_dqinfo)) { |
214 | quota_error(sb, "Can't write info structure" ); |
215 | return size < 0 ? size : -EIO; |
216 | } |
217 | return 0; |
218 | } |
219 | |
220 | static void v2r0_disk2memdqb(struct dquot *dquot, void *dp) |
221 | { |
222 | struct v2r0_disk_dqblk *d = dp, empty; |
223 | struct mem_dqblk *m = &dquot->dq_dqb; |
224 | |
225 | m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit); |
226 | m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit); |
227 | m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes); |
228 | m->dqb_itime = le64_to_cpu(d->dqb_itime); |
229 | m->dqb_bhardlimit = v2_qbtos(le32_to_cpu(d->dqb_bhardlimit)); |
230 | m->dqb_bsoftlimit = v2_qbtos(le32_to_cpu(d->dqb_bsoftlimit)); |
231 | m->dqb_curspace = le64_to_cpu(d->dqb_curspace); |
232 | m->dqb_btime = le64_to_cpu(d->dqb_btime); |
233 | /* We need to escape back all-zero structure */ |
234 | memset(&empty, 0, sizeof(struct v2r0_disk_dqblk)); |
235 | empty.dqb_itime = cpu_to_le64(1); |
236 | if (!memcmp(p: &empty, q: dp, size: sizeof(struct v2r0_disk_dqblk))) |
237 | m->dqb_itime = 0; |
238 | } |
239 | |
240 | static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot) |
241 | { |
242 | struct v2r0_disk_dqblk *d = dp; |
243 | struct mem_dqblk *m = &dquot->dq_dqb; |
244 | struct qtree_mem_dqinfo *info = |
245 | sb_dqinfo(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv; |
246 | |
247 | d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit); |
248 | d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit); |
249 | d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes); |
250 | d->dqb_itime = cpu_to_le64(m->dqb_itime); |
251 | d->dqb_bhardlimit = cpu_to_le32(v2_stoqb(m->dqb_bhardlimit)); |
252 | d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit)); |
253 | d->dqb_curspace = cpu_to_le64(m->dqb_curspace); |
254 | d->dqb_btime = cpu_to_le64(m->dqb_btime); |
255 | d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id)); |
256 | if (qtree_entry_unused(info, disk: dp)) |
257 | d->dqb_itime = cpu_to_le64(1); |
258 | } |
259 | |
260 | static int v2r0_is_id(void *dp, struct dquot *dquot) |
261 | { |
262 | struct v2r0_disk_dqblk *d = dp; |
263 | struct qtree_mem_dqinfo *info = |
264 | sb_dqinfo(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv; |
265 | |
266 | if (qtree_entry_unused(info, disk: dp)) |
267 | return 0; |
268 | return qid_eq(left: make_kqid(from: &init_user_ns, type: dquot->dq_id.type, |
269 | le32_to_cpu(d->dqb_id)), |
270 | right: dquot->dq_id); |
271 | } |
272 | |
273 | static void v2r1_disk2memdqb(struct dquot *dquot, void *dp) |
274 | { |
275 | struct v2r1_disk_dqblk *d = dp, empty; |
276 | struct mem_dqblk *m = &dquot->dq_dqb; |
277 | |
278 | m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit); |
279 | m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit); |
280 | m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes); |
281 | m->dqb_itime = le64_to_cpu(d->dqb_itime); |
282 | m->dqb_bhardlimit = v2_qbtos(le64_to_cpu(d->dqb_bhardlimit)); |
283 | m->dqb_bsoftlimit = v2_qbtos(le64_to_cpu(d->dqb_bsoftlimit)); |
284 | m->dqb_curspace = le64_to_cpu(d->dqb_curspace); |
285 | m->dqb_btime = le64_to_cpu(d->dqb_btime); |
286 | /* We need to escape back all-zero structure */ |
287 | memset(&empty, 0, sizeof(struct v2r1_disk_dqblk)); |
288 | empty.dqb_itime = cpu_to_le64(1); |
289 | if (!memcmp(p: &empty, q: dp, size: sizeof(struct v2r1_disk_dqblk))) |
290 | m->dqb_itime = 0; |
291 | } |
292 | |
293 | static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot) |
294 | { |
295 | struct v2r1_disk_dqblk *d = dp; |
296 | struct mem_dqblk *m = &dquot->dq_dqb; |
297 | struct qtree_mem_dqinfo *info = |
298 | sb_dqinfo(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv; |
299 | |
300 | d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit); |
301 | d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit); |
302 | d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes); |
303 | d->dqb_itime = cpu_to_le64(m->dqb_itime); |
304 | d->dqb_bhardlimit = cpu_to_le64(v2_stoqb(m->dqb_bhardlimit)); |
305 | d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit)); |
306 | d->dqb_curspace = cpu_to_le64(m->dqb_curspace); |
307 | d->dqb_btime = cpu_to_le64(m->dqb_btime); |
308 | d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id)); |
309 | d->dqb_pad = 0; |
310 | if (qtree_entry_unused(info, disk: dp)) |
311 | d->dqb_itime = cpu_to_le64(1); |
312 | } |
313 | |
314 | static int v2r1_is_id(void *dp, struct dquot *dquot) |
315 | { |
316 | struct v2r1_disk_dqblk *d = dp; |
317 | struct qtree_mem_dqinfo *info = |
318 | sb_dqinfo(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv; |
319 | |
320 | if (qtree_entry_unused(info, disk: dp)) |
321 | return 0; |
322 | return qid_eq(left: make_kqid(from: &init_user_ns, type: dquot->dq_id.type, |
323 | le32_to_cpu(d->dqb_id)), |
324 | right: dquot->dq_id); |
325 | } |
326 | |
327 | static int v2_read_dquot(struct dquot *dquot) |
328 | { |
329 | struct quota_info *dqopt = sb_dqopt(sb: dquot->dq_sb); |
330 | int ret; |
331 | |
332 | down_read(sem: &dqopt->dqio_sem); |
333 | ret = qtree_read_dquot( |
334 | info: sb_dqinfo(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv, |
335 | dquot); |
336 | up_read(sem: &dqopt->dqio_sem); |
337 | return ret; |
338 | } |
339 | |
340 | static int v2_write_dquot(struct dquot *dquot) |
341 | { |
342 | struct quota_info *dqopt = sb_dqopt(sb: dquot->dq_sb); |
343 | int ret; |
344 | bool alloc = false; |
345 | |
346 | /* |
347 | * If space for dquot is already allocated, we don't need any |
348 | * protection as we'll only overwrite the place of dquot. We are |
349 | * still protected by concurrent writes of the same dquot by |
350 | * dquot->dq_lock. |
351 | */ |
352 | if (!dquot->dq_off) { |
353 | alloc = true; |
354 | down_write(sem: &dqopt->dqio_sem); |
355 | } else { |
356 | down_read(sem: &dqopt->dqio_sem); |
357 | } |
358 | ret = qtree_write_dquot( |
359 | info: sb_dqinfo(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv, |
360 | dquot); |
361 | if (alloc) |
362 | up_write(sem: &dqopt->dqio_sem); |
363 | else |
364 | up_read(sem: &dqopt->dqio_sem); |
365 | return ret; |
366 | } |
367 | |
368 | static int v2_release_dquot(struct dquot *dquot) |
369 | { |
370 | struct quota_info *dqopt = sb_dqopt(sb: dquot->dq_sb); |
371 | int ret; |
372 | |
373 | down_write(sem: &dqopt->dqio_sem); |
374 | ret = qtree_release_dquot(info: sb_dqinfo(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv, dquot); |
375 | up_write(sem: &dqopt->dqio_sem); |
376 | |
377 | return ret; |
378 | } |
379 | |
380 | static int v2_free_file_info(struct super_block *sb, int type) |
381 | { |
382 | kfree(objp: sb_dqinfo(sb, type)->dqi_priv); |
383 | return 0; |
384 | } |
385 | |
386 | static int v2_get_next_id(struct super_block *sb, struct kqid *qid) |
387 | { |
388 | struct quota_info *dqopt = sb_dqopt(sb); |
389 | int ret; |
390 | |
391 | down_read(sem: &dqopt->dqio_sem); |
392 | ret = qtree_get_next_id(info: sb_dqinfo(sb, type: qid->type)->dqi_priv, qid); |
393 | up_read(sem: &dqopt->dqio_sem); |
394 | return ret; |
395 | } |
396 | |
397 | static const struct quota_format_ops v2_format_ops = { |
398 | .check_quota_file = v2_check_quota_file, |
399 | .read_file_info = v2_read_file_info, |
400 | .write_file_info = v2_write_file_info, |
401 | .free_file_info = v2_free_file_info, |
402 | .read_dqblk = v2_read_dquot, |
403 | .commit_dqblk = v2_write_dquot, |
404 | .release_dqblk = v2_release_dquot, |
405 | .get_next_id = v2_get_next_id, |
406 | }; |
407 | |
408 | static struct quota_format_type v2r0_quota_format = { |
409 | .qf_fmt_id = QFMT_VFS_V0, |
410 | .qf_ops = &v2_format_ops, |
411 | .qf_owner = THIS_MODULE |
412 | }; |
413 | |
414 | static struct quota_format_type v2r1_quota_format = { |
415 | .qf_fmt_id = QFMT_VFS_V1, |
416 | .qf_ops = &v2_format_ops, |
417 | .qf_owner = THIS_MODULE |
418 | }; |
419 | |
420 | static int __init init_v2_quota_format(void) |
421 | { |
422 | int ret; |
423 | |
424 | ret = register_quota_format(fmt: &v2r0_quota_format); |
425 | if (ret) |
426 | return ret; |
427 | return register_quota_format(fmt: &v2r1_quota_format); |
428 | } |
429 | |
430 | static void __exit exit_v2_quota_format(void) |
431 | { |
432 | unregister_quota_format(fmt: &v2r0_quota_format); |
433 | unregister_quota_format(fmt: &v2r1_quota_format); |
434 | } |
435 | |
436 | module_init(init_v2_quota_format); |
437 | module_exit(exit_v2_quota_format); |
438 | |