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 | unsigned int memalloc; |
100 | int ret; |
101 | |
102 | down_read(sem: &dqopt->dqio_sem); |
103 | memalloc = memalloc_nofs_save(); |
104 | ret = v2_read_header(sb, type, dqhead: &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 | else |
121 | ret = -EIO; |
122 | goto out; |
123 | } |
124 | info->dqi_priv = kmalloc(size: 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 but |
137 | * quota core supports only signed 64-bit values so use that |
138 | * as a limit |
139 | */ |
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(info: 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(inode: 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; |
185 | out_free: |
186 | if (ret) { |
187 | kfree(objp: info->dqi_priv); |
188 | info->dqi_priv = NULL; |
189 | } |
190 | out: |
191 | memalloc_nofs_restore(flags: memalloc); |
192 | up_read(sem: &dqopt->dqio_sem); |
193 | return ret; |
194 | } |
195 | |
196 | /* Write information header to quota file */ |
197 | static 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(sem: &dqopt->dqio_sem); |
207 | memalloc = memalloc_nofs_save(); |
208 | spin_lock(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(lock: &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(flags: memalloc); |
221 | up_write(sem: &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 | |
229 | static 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(p: &empty, q: dp, size: sizeof(struct v2r0_disk_dqblk))) |
246 | m->dqb_itime = 0; |
247 | } |
248 | |
249 | static 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(sb: dquot->dq_sb, type: 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, disk: dp)) |
266 | d->dqb_itime = cpu_to_le64(1); |
267 | } |
268 | |
269 | static 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(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv; |
274 | |
275 | if (qtree_entry_unused(info, disk: dp)) |
276 | return 0; |
277 | return qid_eq(left: make_kqid(from: &init_user_ns, type: dquot->dq_id.type, |
278 | le32_to_cpu(d->dqb_id)), |
279 | right: dquot->dq_id); |
280 | } |
281 | |
282 | static 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(p: &empty, q: dp, size: sizeof(struct v2r1_disk_dqblk))) |
299 | m->dqb_itime = 0; |
300 | } |
301 | |
302 | static 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(sb: dquot->dq_sb, type: 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, disk: dp)) |
320 | d->dqb_itime = cpu_to_le64(1); |
321 | } |
322 | |
323 | static 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(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv; |
328 | |
329 | if (qtree_entry_unused(info, disk: dp)) |
330 | return 0; |
331 | return qid_eq(left: make_kqid(from: &init_user_ns, type: dquot->dq_id.type, |
332 | le32_to_cpu(d->dqb_id)), |
333 | right: dquot->dq_id); |
334 | } |
335 | |
336 | static int v2_read_dquot(struct dquot *dquot) |
337 | { |
338 | struct quota_info *dqopt = sb_dqopt(sb: dquot->dq_sb); |
339 | int ret; |
340 | unsigned int memalloc; |
341 | |
342 | down_read(sem: &dqopt->dqio_sem); |
343 | memalloc = memalloc_nofs_save(); |
344 | ret = qtree_read_dquot( |
345 | info: sb_dqinfo(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv, |
346 | dquot); |
347 | memalloc_nofs_restore(flags: memalloc); |
348 | up_read(sem: &dqopt->dqio_sem); |
349 | return ret; |
350 | } |
351 | |
352 | static int v2_write_dquot(struct dquot *dquot) |
353 | { |
354 | struct quota_info *dqopt = sb_dqopt(sb: 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 any |
361 | * protection as we'll only overwrite the place of dquot. We are |
362 | * still protected by concurrent writes of the same dquot by |
363 | * dquot->dq_lock. |
364 | */ |
365 | if (!dquot->dq_off) { |
366 | alloc = true; |
367 | down_write(sem: &dqopt->dqio_sem); |
368 | } else { |
369 | down_read(sem: &dqopt->dqio_sem); |
370 | } |
371 | memalloc = memalloc_nofs_save(); |
372 | ret = qtree_write_dquot( |
373 | info: sb_dqinfo(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv, |
374 | dquot); |
375 | memalloc_nofs_restore(flags: memalloc); |
376 | if (alloc) |
377 | up_write(sem: &dqopt->dqio_sem); |
378 | else |
379 | up_read(sem: &dqopt->dqio_sem); |
380 | return ret; |
381 | } |
382 | |
383 | static int v2_release_dquot(struct dquot *dquot) |
384 | { |
385 | struct quota_info *dqopt = sb_dqopt(sb: dquot->dq_sb); |
386 | unsigned int memalloc; |
387 | int ret; |
388 | |
389 | down_write(sem: &dqopt->dqio_sem); |
390 | memalloc = memalloc_nofs_save(); |
391 | ret = qtree_release_dquot(info: sb_dqinfo(sb: dquot->dq_sb, type: dquot->dq_id.type)->dqi_priv, dquot); |
392 | memalloc_nofs_restore(flags: memalloc); |
393 | up_write(sem: &dqopt->dqio_sem); |
394 | |
395 | return ret; |
396 | } |
397 | |
398 | static int v2_free_file_info(struct super_block *sb, int type) |
399 | { |
400 | kfree(objp: sb_dqinfo(sb, type)->dqi_priv); |
401 | return 0; |
402 | } |
403 | |
404 | static 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(sem: &dqopt->dqio_sem); |
411 | memalloc = memalloc_nofs_save(); |
412 | ret = qtree_get_next_id(info: sb_dqinfo(sb, type: qid->type)->dqi_priv, qid); |
413 | memalloc_nofs_restore(flags: memalloc); |
414 | up_read(sem: &dqopt->dqio_sem); |
415 | return ret; |
416 | } |
417 | |
418 | static 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 | |
429 | static struct quota_format_type v2r0_quota_format = { |
430 | .qf_fmt_id = QFMT_VFS_V0, |
431 | .qf_ops = &v2_format_ops, |
432 | .qf_owner = THIS_MODULE |
433 | }; |
434 | |
435 | static struct quota_format_type v2r1_quota_format = { |
436 | .qf_fmt_id = QFMT_VFS_V1, |
437 | .qf_ops = &v2_format_ops, |
438 | .qf_owner = THIS_MODULE |
439 | }; |
440 | |
441 | static int __init init_v2_quota_format(void) |
442 | { |
443 | int ret; |
444 | |
445 | ret = register_quota_format(fmt: &v2r0_quota_format); |
446 | if (ret) |
447 | return ret; |
448 | return register_quota_format(fmt: &v2r1_quota_format); |
449 | } |
450 | |
451 | static void __exit exit_v2_quota_format(void) |
452 | { |
453 | unregister_quota_format(fmt: &v2r0_quota_format); |
454 | unregister_quota_format(fmt: &v2r1_quota_format); |
455 | } |
456 | |
457 | module_init(init_v2_quota_format); |
458 | module_exit(exit_v2_quota_format); |
459 | |