]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Jan Kara <jack@suse.cz> |
2 | References: fate#302681 | |
3 | Subject: [PATCH 12/28] quota: Split off quota tree handling into a separate file | |
4 | Patch-mainline: 2.6.29? | |
5 | ||
6 | There is going to be a new version of quota format having 64-bit | |
7 | quota limits and a new quota format for OCFS2. They are both | |
8 | going to use the same tree structure as VFSv0 quota format. So | |
9 | split out tree handling into a separate file and make size of | |
10 | leaf blocks, amount of space usable in each block (needed for | |
11 | checksumming) and structures contained in them configurable | |
12 | so that the code can be shared. | |
13 | ||
14 | Signed-off-by: Jan Kara <jack@suse.cz> | |
15 | --- | |
16 | fs/Kconfig | 5 | |
17 | fs/Makefile | 1 | |
18 | fs/quota_tree.c | 645 ++++++++++++++++++++++++++++++++++++++++++++ | |
19 | fs/quota_tree.h | 25 + | |
20 | fs/quota_v2.c | 598 +++------------------------------------- | |
21 | fs/quotaio_v2.h | 33 -- | |
22 | include/linux/dqblk_qtree.h | 56 +++ | |
23 | include/linux/dqblk_v2.h | 19 - | |
24 | 8 files changed, 800 insertions(+), 582 deletions(-) | |
25 | create mode 100644 fs/quota_tree.c | |
26 | create mode 100644 fs/quota_tree.h | |
27 | create mode 100644 include/linux/dqblk_qtree.h | |
28 | ||
29 | --- a/fs/Kconfig | |
30 | +++ b/fs/Kconfig | |
31 | @@ -588,6 +588,10 @@ config PRINT_QUOTA_WARNING | |
32 | Note that this behavior is currently deprecated and may go away in | |
33 | future. Please use notification via netlink socket instead. | |
34 | ||
35 | +# Generic support for tree structured quota files. Seleted when needed. | |
36 | +config QUOTA_TREE | |
37 | + tristate | |
38 | + | |
39 | config QFMT_V1 | |
40 | tristate "Old quota format support" | |
41 | depends on QUOTA | |
42 | @@ -599,6 +603,7 @@ config QFMT_V1 | |
43 | config QFMT_V2 | |
44 | tristate "Quota format v2 support" | |
45 | depends on QUOTA | |
46 | + select QUOTA_TREE | |
47 | help | |
48 | This quota format allows using quotas with 32-bit UIDs/GIDs. If you | |
49 | need this functionality say Y here. | |
50 | --- a/fs/Makefile | |
51 | +++ b/fs/Makefile | |
52 | @@ -53,6 +53,7 @@ obj-$(CONFIG_GENERIC_ACL) += generic_acl | |
53 | obj-$(CONFIG_QUOTA) += dquot.o | |
54 | obj-$(CONFIG_QFMT_V1) += quota_v1.o | |
55 | obj-$(CONFIG_QFMT_V2) += quota_v2.o | |
56 | +obj-$(CONFIG_QUOTA_TREE) += quota_tree.o | |
57 | obj-$(CONFIG_QUOTACTL) += quota.o | |
58 | ||
59 | obj-$(CONFIG_DNOTIFY) += dnotify.o | |
60 | --- /dev/null | |
61 | +++ b/fs/quota_tree.c | |
62 | @@ -0,0 +1,645 @@ | |
63 | +/* | |
64 | + * vfsv0 quota IO operations on file | |
65 | + */ | |
66 | + | |
67 | +#include <linux/errno.h> | |
68 | +#include <linux/fs.h> | |
69 | +#include <linux/mount.h> | |
70 | +#include <linux/dqblk_v2.h> | |
71 | +#include <linux/kernel.h> | |
72 | +#include <linux/init.h> | |
73 | +#include <linux/module.h> | |
74 | +#include <linux/slab.h> | |
75 | +#include <linux/quotaops.h> | |
76 | + | |
77 | +#include <asm/byteorder.h> | |
78 | + | |
79 | +#include "quota_tree.h" | |
80 | + | |
81 | +MODULE_AUTHOR("Jan Kara"); | |
82 | +MODULE_DESCRIPTION("Quota trie support"); | |
83 | +MODULE_LICENSE("GPL"); | |
84 | + | |
85 | +#define __QUOTA_QT_PARANOIA | |
86 | + | |
87 | +typedef char *dqbuf_t; | |
88 | + | |
89 | +static int get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth) | |
90 | +{ | |
91 | + unsigned int epb = info->dqi_usable_bs >> 2; | |
92 | + | |
93 | + depth = info->dqi_qtree_depth - depth - 1; | |
94 | + while (depth--) | |
95 | + id /= epb; | |
96 | + return id % epb; | |
97 | +} | |
98 | + | |
99 | +/* Number of entries in one blocks */ | |
100 | +static inline int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info) | |
101 | +{ | |
102 | + return (info->dqi_usable_bs - sizeof(struct qt_disk_dqdbheader)) | |
103 | + / info->dqi_entry_size; | |
104 | +} | |
105 | + | |
106 | +static dqbuf_t getdqbuf(size_t size) | |
107 | +{ | |
108 | + dqbuf_t buf = kmalloc(size, GFP_NOFS); | |
109 | + if (!buf) | |
110 | + printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n"); | |
111 | + return buf; | |
112 | +} | |
113 | + | |
114 | +static inline void freedqbuf(dqbuf_t buf) | |
115 | +{ | |
116 | + kfree(buf); | |
117 | +} | |
118 | + | |
119 | +static inline ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, dqbuf_t buf) | |
120 | +{ | |
121 | + struct super_block *sb = info->dqi_sb; | |
122 | + | |
123 | + memset(buf, 0, info->dqi_usable_bs); | |
124 | + return sb->s_op->quota_read(sb, info->dqi_type, (char *)buf, | |
125 | + info->dqi_usable_bs, blk << info->dqi_blocksize_bits); | |
126 | +} | |
127 | + | |
128 | +static inline ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, dqbuf_t buf) | |
129 | +{ | |
130 | + struct super_block *sb = info->dqi_sb; | |
131 | + | |
132 | + return sb->s_op->quota_write(sb, info->dqi_type, (char *)buf, | |
133 | + info->dqi_usable_bs, blk << info->dqi_blocksize_bits); | |
134 | +} | |
135 | + | |
136 | +/* Remove empty block from list and return it */ | |
137 | +static int get_free_dqblk(struct qtree_mem_dqinfo *info) | |
138 | +{ | |
139 | + dqbuf_t buf = getdqbuf(info->dqi_usable_bs); | |
140 | + struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; | |
141 | + int ret, blk; | |
142 | + | |
143 | + if (!buf) | |
144 | + return -ENOMEM; | |
145 | + if (info->dqi_free_blk) { | |
146 | + blk = info->dqi_free_blk; | |
147 | + ret = read_blk(info, blk, buf); | |
148 | + if (ret < 0) | |
149 | + goto out_buf; | |
150 | + info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free); | |
151 | + } | |
152 | + else { | |
153 | + memset(buf, 0, info->dqi_usable_bs); | |
154 | + /* Assure block allocation... */ | |
155 | + ret = write_blk(info, info->dqi_blocks, buf); | |
156 | + if (ret < 0) | |
157 | + goto out_buf; | |
158 | + blk = info->dqi_blocks++; | |
159 | + } | |
160 | + mark_info_dirty(info->dqi_sb, info->dqi_type); | |
161 | + ret = blk; | |
162 | +out_buf: | |
163 | + freedqbuf(buf); | |
164 | + return ret; | |
165 | +} | |
166 | + | |
167 | +/* Insert empty block to the list */ | |
168 | +static int put_free_dqblk(struct qtree_mem_dqinfo *info, dqbuf_t buf, uint blk) | |
169 | +{ | |
170 | + struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; | |
171 | + int err; | |
172 | + | |
173 | + dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk); | |
174 | + dh->dqdh_prev_free = cpu_to_le32(0); | |
175 | + dh->dqdh_entries = cpu_to_le16(0); | |
176 | + err = write_blk(info, blk, buf); | |
177 | + if (err < 0) | |
178 | + return err; | |
179 | + info->dqi_free_blk = blk; | |
180 | + mark_info_dirty(info->dqi_sb, info->dqi_type); | |
181 | + return 0; | |
182 | +} | |
183 | + | |
184 | +/* Remove given block from the list of blocks with free entries */ | |
185 | +static int remove_free_dqentry(struct qtree_mem_dqinfo *info, dqbuf_t buf, uint blk) | |
186 | +{ | |
187 | + dqbuf_t tmpbuf = getdqbuf(info->dqi_usable_bs); | |
188 | + struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; | |
189 | + uint nextblk = le32_to_cpu(dh->dqdh_next_free); | |
190 | + uint prevblk = le32_to_cpu(dh->dqdh_prev_free); | |
191 | + int err; | |
192 | + | |
193 | + if (!tmpbuf) | |
194 | + return -ENOMEM; | |
195 | + if (nextblk) { | |
196 | + err = read_blk(info, nextblk, tmpbuf); | |
197 | + if (err < 0) | |
198 | + goto out_buf; | |
199 | + ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = | |
200 | + dh->dqdh_prev_free; | |
201 | + err = write_blk(info, nextblk, tmpbuf); | |
202 | + if (err < 0) | |
203 | + goto out_buf; | |
204 | + } | |
205 | + if (prevblk) { | |
206 | + err = read_blk(info, prevblk, tmpbuf); | |
207 | + if (err < 0) | |
208 | + goto out_buf; | |
209 | + ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free = | |
210 | + dh->dqdh_next_free; | |
211 | + err = write_blk(info, prevblk, tmpbuf); | |
212 | + if (err < 0) | |
213 | + goto out_buf; | |
214 | + } else { | |
215 | + info->dqi_free_entry = nextblk; | |
216 | + mark_info_dirty(info->dqi_sb, info->dqi_type); | |
217 | + } | |
218 | + freedqbuf(tmpbuf); | |
219 | + dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0); | |
220 | + /* No matter whether write succeeds block is out of list */ | |
221 | + if (write_blk(info, blk, buf) < 0) | |
222 | + printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk); | |
223 | + return 0; | |
224 | +out_buf: | |
225 | + freedqbuf(tmpbuf); | |
226 | + return err; | |
227 | +} | |
228 | + | |
229 | +/* Insert given block to the beginning of list with free entries */ | |
230 | +static int insert_free_dqentry(struct qtree_mem_dqinfo *info, dqbuf_t buf, uint blk) | |
231 | +{ | |
232 | + dqbuf_t tmpbuf = getdqbuf(info->dqi_usable_bs); | |
233 | + struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; | |
234 | + int err; | |
235 | + | |
236 | + if (!tmpbuf) | |
237 | + return -ENOMEM; | |
238 | + dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry); | |
239 | + dh->dqdh_prev_free = cpu_to_le32(0); | |
240 | + err = write_blk(info, blk, buf); | |
241 | + if (err < 0) | |
242 | + goto out_buf; | |
243 | + if (info->dqi_free_entry) { | |
244 | + err = read_blk(info, info->dqi_free_entry, tmpbuf); | |
245 | + if (err < 0) | |
246 | + goto out_buf; | |
247 | + ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = | |
248 | + cpu_to_le32(blk); | |
249 | + err = write_blk(info, info->dqi_free_entry, tmpbuf); | |
250 | + if (err < 0) | |
251 | + goto out_buf; | |
252 | + } | |
253 | + freedqbuf(tmpbuf); | |
254 | + info->dqi_free_entry = blk; | |
255 | + mark_info_dirty(info->dqi_sb, info->dqi_type); | |
256 | + return 0; | |
257 | +out_buf: | |
258 | + freedqbuf(tmpbuf); | |
259 | + return err; | |
260 | +} | |
261 | + | |
262 | +/* Is the entry in the block free? */ | |
263 | +int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk) | |
264 | +{ | |
265 | + int i; | |
266 | + | |
267 | + for (i = 0; i < info->dqi_entry_size; i++) | |
268 | + if (disk[i]) | |
269 | + return 0; | |
270 | + return 1; | |
271 | +} | |
272 | +EXPORT_SYMBOL(qtree_entry_unused); | |
273 | + | |
274 | +/* Find space for dquot */ | |
275 | +static uint find_free_dqentry(struct qtree_mem_dqinfo *info, | |
276 | + struct dquot *dquot, int *err) | |
277 | +{ | |
278 | + uint blk, i; | |
279 | + struct qt_disk_dqdbheader *dh; | |
280 | + dqbuf_t buf = getdqbuf(info->dqi_usable_bs); | |
281 | + char *ddquot; | |
282 | + | |
283 | + *err = 0; | |
284 | + if (!buf) { | |
285 | + *err = -ENOMEM; | |
286 | + return 0; | |
287 | + } | |
288 | + dh = (struct qt_disk_dqdbheader *)buf; | |
289 | + if (info->dqi_free_entry) { | |
290 | + blk = info->dqi_free_entry; | |
291 | + *err = read_blk(info, blk, buf); | |
292 | + if (*err < 0) | |
293 | + goto out_buf; | |
294 | + } else { | |
295 | + blk = get_free_dqblk(info); | |
296 | + if ((int)blk < 0) { | |
297 | + *err = blk; | |
298 | + freedqbuf(buf); | |
299 | + return 0; | |
300 | + } | |
301 | + memset(buf, 0, info->dqi_usable_bs); | |
302 | + /* This is enough as block is already zeroed and entry list is empty... */ | |
303 | + info->dqi_free_entry = blk; | |
304 | + mark_info_dirty(dquot->dq_sb, dquot->dq_type); | |
305 | + } | |
306 | + /* Block will be full? */ | |
307 | + if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) { | |
308 | + *err = remove_free_dqentry(info, buf, blk); | |
309 | + if (*err < 0) { | |
310 | + printk(KERN_ERR "VFS: find_free_dqentry(): Can't " | |
311 | + "remove block (%u) from entry free list.\n", | |
312 | + blk); | |
313 | + goto out_buf; | |
314 | + } | |
315 | + } | |
316 | + le16_add_cpu(&dh->dqdh_entries, 1); | |
317 | + /* Find free structure in block */ | |
318 | + for (i = 0, ddquot = ((char *)buf) + sizeof(struct qt_disk_dqdbheader); | |
319 | + i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot); | |
320 | + i++, ddquot += info->dqi_entry_size); | |
321 | +#ifdef __QUOTA_QT_PARANOIA | |
322 | + if (i == qtree_dqstr_in_blk(info)) { | |
323 | + printk(KERN_ERR "VFS: find_free_dqentry(): Data block full " | |
324 | + "but it shouldn't.\n"); | |
325 | + *err = -EIO; | |
326 | + goto out_buf; | |
327 | + } | |
328 | +#endif | |
329 | + *err = write_blk(info, blk, buf); | |
330 | + if (*err < 0) { | |
331 | + printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota " | |
332 | + "data block %u.\n", blk); | |
333 | + goto out_buf; | |
334 | + } | |
335 | + dquot->dq_off = (blk << info->dqi_blocksize_bits) + | |
336 | + sizeof(struct qt_disk_dqdbheader) + | |
337 | + i * info->dqi_entry_size; | |
338 | + freedqbuf(buf); | |
339 | + return blk; | |
340 | +out_buf: | |
341 | + freedqbuf(buf); | |
342 | + return 0; | |
343 | +} | |
344 | + | |
345 | +/* Insert reference to structure into the trie */ | |
346 | +static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, | |
347 | + uint *treeblk, int depth) | |
348 | +{ | |
349 | + dqbuf_t buf = getdqbuf(info->dqi_usable_bs); | |
350 | + int ret = 0, newson = 0, newact = 0; | |
351 | + __le32 *ref; | |
352 | + uint newblk; | |
353 | + | |
354 | + if (!buf) | |
355 | + return -ENOMEM; | |
356 | + if (!*treeblk) { | |
357 | + ret = get_free_dqblk(info); | |
358 | + if (ret < 0) | |
359 | + goto out_buf; | |
360 | + *treeblk = ret; | |
361 | + memset(buf, 0, info->dqi_usable_bs); | |
362 | + newact = 1; | |
363 | + } else { | |
364 | + ret = read_blk(info, *treeblk, buf); | |
365 | + if (ret < 0) { | |
366 | + printk(KERN_ERR "VFS: Can't read tree quota block " | |
367 | + "%u.\n", *treeblk); | |
368 | + goto out_buf; | |
369 | + } | |
370 | + } | |
371 | + ref = (__le32 *)buf; | |
372 | + newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); | |
373 | + if (!newblk) | |
374 | + newson = 1; | |
375 | + if (depth == info->dqi_qtree_depth - 1) { | |
376 | +#ifdef __QUOTA_QT_PARANOIA | |
377 | + if (newblk) { | |
378 | + printk(KERN_ERR "VFS: Inserting already present quota " | |
379 | + "entry (block %u).\n", | |
380 | + le32_to_cpu(ref[get_index(info, | |
381 | + dquot->dq_id, depth)])); | |
382 | + ret = -EIO; | |
383 | + goto out_buf; | |
384 | + } | |
385 | +#endif | |
386 | + newblk = find_free_dqentry(info, dquot, &ret); | |
387 | + } else { | |
388 | + ret = do_insert_tree(info, dquot, &newblk, depth+1); | |
389 | + } | |
390 | + if (newson && ret >= 0) { | |
391 | + ref[get_index(info, dquot->dq_id, depth)] = | |
392 | + cpu_to_le32(newblk); | |
393 | + ret = write_blk(info, *treeblk, buf); | |
394 | + } else if (newact && ret < 0) { | |
395 | + put_free_dqblk(info, buf, *treeblk); | |
396 | + } | |
397 | +out_buf: | |
398 | + freedqbuf(buf); | |
399 | + return ret; | |
400 | +} | |
401 | + | |
402 | +/* Wrapper for inserting quota structure into tree */ | |
403 | +static inline int dq_insert_tree(struct qtree_mem_dqinfo *info, | |
404 | + struct dquot *dquot) | |
405 | +{ | |
406 | + int tmp = QT_TREEOFF; | |
407 | + return do_insert_tree(info, dquot, &tmp, 0); | |
408 | +} | |
409 | + | |
410 | +/* | |
411 | + * We don't have to be afraid of deadlocks as we never have quotas on quota files... | |
412 | + */ | |
413 | +int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) | |
414 | +{ | |
415 | + int type = dquot->dq_type; | |
416 | + struct super_block *sb = dquot->dq_sb; | |
417 | + ssize_t ret; | |
418 | + dqbuf_t ddquot = getdqbuf(info->dqi_entry_size); | |
419 | + | |
420 | + if (!ddquot) | |
421 | + return -ENOMEM; | |
422 | + | |
423 | + /* dq_off is guarded by dqio_mutex */ | |
424 | + if (!dquot->dq_off) { | |
425 | + ret = dq_insert_tree(info, dquot); | |
426 | + if (ret < 0) { | |
427 | + printk(KERN_ERR "VFS: Error %zd occurred while " | |
428 | + "creating quota.\n", ret); | |
429 | + freedqbuf(ddquot); | |
430 | + return ret; | |
431 | + } | |
432 | + } | |
433 | + spin_lock(&dq_data_lock); | |
434 | + info->dqi_ops->mem2disk_dqblk(ddquot, dquot); | |
435 | + spin_unlock(&dq_data_lock); | |
436 | + ret = sb->s_op->quota_write(sb, type, (char *)ddquot, | |
437 | + info->dqi_entry_size, dquot->dq_off); | |
438 | + if (ret != info->dqi_entry_size) { | |
439 | + printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", | |
440 | + sb->s_id); | |
441 | + if (ret >= 0) | |
442 | + ret = -ENOSPC; | |
443 | + } else { | |
444 | + ret = 0; | |
445 | + } | |
446 | + dqstats.writes++; | |
447 | + freedqbuf(ddquot); | |
448 | + | |
449 | + return ret; | |
450 | +} | |
451 | +EXPORT_SYMBOL(qtree_write_dquot); | |
452 | + | |
453 | +/* Free dquot entry in data block */ | |
454 | +static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot, | |
455 | + uint blk) | |
456 | +{ | |
457 | + struct qt_disk_dqdbheader *dh; | |
458 | + dqbuf_t buf = getdqbuf(info->dqi_usable_bs); | |
459 | + int ret = 0; | |
460 | + | |
461 | + if (!buf) | |
462 | + return -ENOMEM; | |
463 | + if (dquot->dq_off >> info->dqi_blocksize_bits != blk) { | |
464 | + printk(KERN_ERR "VFS: Quota structure has offset to other " | |
465 | + "block (%u) than it should (%u).\n", blk, | |
466 | + (uint)(dquot->dq_off >> info->dqi_blocksize_bits)); | |
467 | + goto out_buf; | |
468 | + } | |
469 | + ret = read_blk(info, blk, buf); | |
470 | + if (ret < 0) { | |
471 | + printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk); | |
472 | + goto out_buf; | |
473 | + } | |
474 | + dh = (struct qt_disk_dqdbheader *)buf; | |
475 | + le16_add_cpu(&dh->dqdh_entries, -1); | |
476 | + if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */ | |
477 | + ret = remove_free_dqentry(info, buf, blk); | |
478 | + if (ret >= 0) | |
479 | + ret = put_free_dqblk(info, buf, blk); | |
480 | + if (ret < 0) { | |
481 | + printk(KERN_ERR "VFS: Can't move quota data block (%u) " | |
482 | + "to free list.\n", blk); | |
483 | + goto out_buf; | |
484 | + } | |
485 | + } else { | |
486 | + memset(buf + | |
487 | + (dquot->dq_off & ((1 << info->dqi_blocksize_bits) - 1)), | |
488 | + 0, info->dqi_entry_size); | |
489 | + if (le16_to_cpu(dh->dqdh_entries) == | |
490 | + qtree_dqstr_in_blk(info) - 1) { | |
491 | + /* Insert will write block itself */ | |
492 | + ret = insert_free_dqentry(info, buf, blk); | |
493 | + if (ret < 0) { | |
494 | + printk(KERN_ERR "VFS: Can't insert quota data " | |
495 | + "block (%u) to free entry list.\n", blk); | |
496 | + goto out_buf; | |
497 | + } | |
498 | + } else { | |
499 | + ret = write_blk(info, blk, buf); | |
500 | + if (ret < 0) { | |
501 | + printk(KERN_ERR "VFS: Can't write quota data " | |
502 | + "block %u\n", blk); | |
503 | + goto out_buf; | |
504 | + } | |
505 | + } | |
506 | + } | |
507 | + dquot->dq_off = 0; /* Quota is now unattached */ | |
508 | +out_buf: | |
509 | + freedqbuf(buf); | |
510 | + return ret; | |
511 | +} | |
512 | + | |
513 | +/* Remove reference to dquot from tree */ | |
514 | +static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, | |
515 | + uint *blk, int depth) | |
516 | +{ | |
517 | + dqbuf_t buf = getdqbuf(info->dqi_usable_bs); | |
518 | + int ret = 0; | |
519 | + uint newblk; | |
520 | + __le32 *ref = (__le32 *)buf; | |
521 | + | |
522 | + if (!buf) | |
523 | + return -ENOMEM; | |
524 | + ret = read_blk(info, *blk, buf); | |
525 | + if (ret < 0) { | |
526 | + printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk); | |
527 | + goto out_buf; | |
528 | + } | |
529 | + newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); | |
530 | + if (depth == info->dqi_qtree_depth - 1) { | |
531 | + ret = free_dqentry(info, dquot, newblk); | |
532 | + newblk = 0; | |
533 | + } else { | |
534 | + ret = remove_tree(info, dquot, &newblk, depth+1); | |
535 | + } | |
536 | + if (ret >= 0 && !newblk) { | |
537 | + int i; | |
538 | + ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0); | |
539 | + /* Block got empty? */ | |
540 | + for (i = 0; | |
541 | + i < (info->dqi_usable_bs >> 2) && !ref[i]; | |
542 | + i++); | |
543 | + /* Don't put the root block into the free block list */ | |
544 | + if (i == (info->dqi_usable_bs >> 2) | |
545 | + && *blk != QT_TREEOFF) { | |
546 | + put_free_dqblk(info, buf, *blk); | |
547 | + *blk = 0; | |
548 | + } else { | |
549 | + ret = write_blk(info, *blk, buf); | |
550 | + if (ret < 0) | |
551 | + printk(KERN_ERR "VFS: Can't write quota tree " | |
552 | + "block %u.\n", *blk); | |
553 | + } | |
554 | + } | |
555 | +out_buf: | |
556 | + freedqbuf(buf); | |
557 | + return ret; | |
558 | +} | |
559 | + | |
560 | +/* Delete dquot from tree */ | |
561 | +int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) | |
562 | +{ | |
563 | + uint tmp = QT_TREEOFF; | |
564 | + | |
565 | + if (!dquot->dq_off) /* Even not allocated? */ | |
566 | + return 0; | |
567 | + return remove_tree(info, dquot, &tmp, 0); | |
568 | +} | |
569 | +EXPORT_SYMBOL(qtree_delete_dquot); | |
570 | + | |
571 | +/* Find entry in block */ | |
572 | +static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info, | |
573 | + struct dquot *dquot, uint blk) | |
574 | +{ | |
575 | + dqbuf_t buf = getdqbuf(info->dqi_usable_bs); | |
576 | + loff_t ret = 0; | |
577 | + int i; | |
578 | + char *ddquot; | |
579 | + | |
580 | + if (!buf) | |
581 | + return -ENOMEM; | |
582 | + ret = read_blk(info, blk, buf); | |
583 | + if (ret < 0) { | |
584 | + printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); | |
585 | + goto out_buf; | |
586 | + } | |
587 | + for (i = 0, ddquot = ((char *)buf) + sizeof(struct qt_disk_dqdbheader); | |
588 | + i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot); | |
589 | + i++, ddquot += info->dqi_entry_size); | |
590 | + if (i == qtree_dqstr_in_blk(info)) { | |
591 | + printk(KERN_ERR "VFS: Quota for id %u referenced " | |
592 | + "but not present.\n", dquot->dq_id); | |
593 | + ret = -EIO; | |
594 | + goto out_buf; | |
595 | + } else { | |
596 | + ret = (blk << info->dqi_blocksize_bits) + sizeof(struct | |
597 | + qt_disk_dqdbheader) + i * info->dqi_entry_size; | |
598 | + } | |
599 | +out_buf: | |
600 | + freedqbuf(buf); | |
601 | + return ret; | |
602 | +} | |
603 | + | |
604 | +/* Find entry for given id in the tree */ | |
605 | +static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info, | |
606 | + struct dquot *dquot, uint blk, int depth) | |
607 | +{ | |
608 | + dqbuf_t buf = getdqbuf(info->dqi_usable_bs); | |
609 | + loff_t ret = 0; | |
610 | + __le32 *ref = (__le32 *)buf; | |
611 | + | |
612 | + if (!buf) | |
613 | + return -ENOMEM; | |
614 | + ret = read_blk(info, blk, buf); | |
615 | + if (ret < 0) { | |
616 | + printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); | |
617 | + goto out_buf; | |
618 | + } | |
619 | + ret = 0; | |
620 | + blk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); | |
621 | + if (!blk) /* No reference? */ | |
622 | + goto out_buf; | |
623 | + if (depth < info->dqi_qtree_depth - 1) | |
624 | + ret = find_tree_dqentry(info, dquot, blk, depth+1); | |
625 | + else | |
626 | + ret = find_block_dqentry(info, dquot, blk); | |
627 | +out_buf: | |
628 | + freedqbuf(buf); | |
629 | + return ret; | |
630 | +} | |
631 | + | |
632 | +/* Find entry for given id in the tree - wrapper function */ | |
633 | +static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info, | |
634 | + struct dquot *dquot) | |
635 | +{ | |
636 | + return find_tree_dqentry(info, dquot, QT_TREEOFF, 0); | |
637 | +} | |
638 | + | |
639 | +int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) | |
640 | +{ | |
641 | + int type = dquot->dq_type; | |
642 | + struct super_block *sb = dquot->dq_sb; | |
643 | + loff_t offset; | |
644 | + dqbuf_t ddquot; | |
645 | + int ret = 0; | |
646 | + | |
647 | +#ifdef __QUOTA_QT_PARANOIA | |
648 | + /* Invalidated quota? */ | |
649 | + if (!sb_dqopt(dquot->dq_sb)->files[type]) { | |
650 | + printk(KERN_ERR "VFS: Quota invalidated while reading!\n"); | |
651 | + return -EIO; | |
652 | + } | |
653 | +#endif | |
654 | + /* Do we know offset of the dquot entry in the quota file? */ | |
655 | + if (!dquot->dq_off) { | |
656 | + offset = find_dqentry(info, dquot); | |
657 | + if (offset <= 0) { /* Entry not present? */ | |
658 | + if (offset < 0) | |
659 | + printk(KERN_ERR "VFS: Can't read quota " | |
660 | + "structure for id %u.\n", dquot->dq_id); | |
661 | + dquot->dq_off = 0; | |
662 | + set_bit(DQ_FAKE_B, &dquot->dq_flags); | |
663 | + memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); | |
664 | + ret = offset; | |
665 | + goto out; | |
666 | + } | |
667 | + dquot->dq_off = offset; | |
668 | + } | |
669 | + ddquot = getdqbuf(info->dqi_entry_size); | |
670 | + if (!ddquot) | |
671 | + return -ENOMEM; | |
672 | + ret = sb->s_op->quota_read(sb, type, (char *)ddquot, | |
673 | + info->dqi_entry_size, dquot->dq_off); | |
674 | + if (ret != info->dqi_entry_size) { | |
675 | + if (ret >= 0) | |
676 | + ret = -EIO; | |
677 | + printk(KERN_ERR "VFS: Error while reading quota " | |
678 | + "structure for id %u.\n", dquot->dq_id); | |
679 | + set_bit(DQ_FAKE_B, &dquot->dq_flags); | |
680 | + memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); | |
681 | + freedqbuf(ddquot); | |
682 | + goto out; | |
683 | + } | |
684 | + spin_lock(&dq_data_lock); | |
685 | + info->dqi_ops->disk2mem_dqblk(dquot, ddquot); | |
686 | + if (!dquot->dq_dqb.dqb_bhardlimit && | |
687 | + !dquot->dq_dqb.dqb_bsoftlimit && | |
688 | + !dquot->dq_dqb.dqb_ihardlimit && | |
689 | + !dquot->dq_dqb.dqb_isoftlimit) | |
690 | + set_bit(DQ_FAKE_B, &dquot->dq_flags); | |
691 | + spin_unlock(&dq_data_lock); | |
692 | + freedqbuf(ddquot); | |
693 | +out: | |
694 | + dqstats.reads++; | |
695 | + return ret; | |
696 | +} | |
697 | +EXPORT_SYMBOL(qtree_read_dquot); | |
698 | + | |
699 | +/* Check whether dquot should not be deleted. We know we are | |
700 | + * the only one operating on dquot (thanks to dq_lock) */ | |
701 | +int qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) | |
702 | +{ | |
703 | + if (test_bit(DQ_FAKE_B, &dquot->dq_flags) && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) | |
704 | + return qtree_delete_dquot(info, dquot); | |
705 | + return 0; | |
706 | +} | |
707 | +EXPORT_SYMBOL(qtree_release_dquot); | |
708 | --- /dev/null | |
709 | +++ b/fs/quota_tree.h | |
710 | @@ -0,0 +1,25 @@ | |
711 | +/* | |
712 | + * Definitions of structures for vfsv0 quota format | |
713 | + */ | |
714 | + | |
715 | +#ifndef _LINUX_QUOTA_TREE_H | |
716 | +#define _LINUX_QUOTA_TREE_H | |
717 | + | |
718 | +#include <linux/types.h> | |
719 | +#include <linux/quota.h> | |
720 | + | |
721 | +/* | |
722 | + * Structure of header of block with quota structures. It is padded to 16 bytes so | |
723 | + * there will be space for exactly 21 quota-entries in a block | |
724 | + */ | |
725 | +struct qt_disk_dqdbheader { | |
726 | + __le32 dqdh_next_free; /* Number of next block with free entry */ | |
727 | + __le32 dqdh_prev_free; /* Number of previous block with free entry */ | |
728 | + __le16 dqdh_entries; /* Number of valid entries in block */ | |
729 | + __le16 dqdh_pad1; | |
730 | + __le32 dqdh_pad2; | |
731 | +}; | |
732 | + | |
733 | +#define QT_TREEOFF 1 /* Offset of tree in file in blocks */ | |
734 | + | |
735 | +#endif /* _LINUX_QUOTAIO_TREE_H */ | |
736 | --- a/fs/quota_v2.c | |
737 | +++ b/fs/quota_v2.c | |
738 | @@ -14,6 +14,7 @@ | |
739 | ||
740 | #include <asm/byteorder.h> | |
741 | ||
742 | +#include "quota_tree.h" | |
743 | #include "quotaio_v2.h" | |
744 | ||
745 | MODULE_AUTHOR("Jan Kara"); | |
746 | @@ -22,10 +23,15 @@ MODULE_LICENSE("GPL"); | |
747 | ||
748 | #define __QUOTA_V2_PARANOIA | |
749 | ||
750 | -typedef char *dqbuf_t; | |
751 | - | |
752 | -#define GETIDINDEX(id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff) | |
753 | -#define GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)buf)+sizeof(struct v2_disk_dqdbheader))) | |
754 | +static void v2_mem2diskdqb(void *dp, struct dquot *dquot); | |
755 | +static void v2_disk2memdqb(struct dquot *dquot, void *dp); | |
756 | +static int v2_is_id(void *dp, struct dquot *dquot); | |
757 | + | |
758 | +static struct qtree_fmt_operations v2_qtree_ops = { | |
759 | + .mem2disk_dqblk = v2_mem2diskdqb, | |
760 | + .disk2mem_dqblk = v2_disk2memdqb, | |
761 | + .is_id = v2_is_id, | |
762 | +}; | |
763 | ||
764 | #define QUOTABLOCK_BITS 10 | |
765 | #define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS) | |
766 | @@ -64,7 +70,7 @@ static int v2_check_quota_file(struct su | |
767 | static int v2_read_file_info(struct super_block *sb, int type) | |
768 | { | |
769 | struct v2_disk_dqinfo dinfo; | |
770 | - struct mem_dqinfo *info = sb_dqopt(sb)->info+type; | |
771 | + struct mem_dqinfo *info = sb_dqinfo(sb, type); | |
772 | ssize_t size; | |
773 | ||
774 | size = sb->s_op->quota_read(sb, type, (char *)&dinfo, | |
775 | @@ -80,9 +86,16 @@ static int v2_read_file_info(struct supe | |
776 | info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); | |
777 | info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace); | |
778 | info->dqi_flags = le32_to_cpu(dinfo.dqi_flags); | |
779 | - info->u.v2_i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks); | |
780 | - info->u.v2_i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk); | |
781 | - info->u.v2_i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry); | |
782 | + info->u.v2_i.i.dqi_sb = sb; | |
783 | + info->u.v2_i.i.dqi_type = type; | |
784 | + info->u.v2_i.i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks); | |
785 | + info->u.v2_i.i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk); | |
786 | + info->u.v2_i.i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry); | |
787 | + info->u.v2_i.i.dqi_blocksize_bits = V2_DQBLKSIZE_BITS; | |
788 | + info->u.v2_i.i.dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS; | |
789 | + info->u.v2_i.i.dqi_qtree_depth = qtree_depth(&info->u.v2_i.i); | |
790 | + info->u.v2_i.i.dqi_entry_size = sizeof(struct v2_disk_dqblk); | |
791 | + info->u.v2_i.i.dqi_ops = &v2_qtree_ops; | |
792 | return 0; | |
793 | } | |
794 | ||
795 | @@ -90,7 +103,7 @@ static int v2_read_file_info(struct supe | |
796 | static int v2_write_file_info(struct super_block *sb, int type) | |
797 | { | |
798 | struct v2_disk_dqinfo dinfo; | |
799 | - struct mem_dqinfo *info = sb_dqopt(sb)->info+type; | |
800 | + struct mem_dqinfo *info = sb_dqinfo(sb, type); | |
801 | ssize_t size; | |
802 | ||
803 | spin_lock(&dq_data_lock); | |
804 | @@ -99,9 +112,9 @@ static int v2_write_file_info(struct sup | |
805 | dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace); | |
806 | dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK); | |
807 | spin_unlock(&dq_data_lock); | |
808 | - dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.dqi_blocks); | |
809 | - dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.dqi_free_blk); | |
810 | - dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.dqi_free_entry); | |
811 | + dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.i.dqi_blocks); | |
812 | + dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.i.dqi_free_blk); | |
813 | + dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.i.dqi_free_entry); | |
814 | size = sb->s_op->quota_write(sb, type, (char *)&dinfo, | |
815 | sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); | |
816 | if (size != sizeof(struct v2_disk_dqinfo)) { | |
817 | @@ -112,8 +125,11 @@ static int v2_write_file_info(struct sup | |
818 | return 0; | |
819 | } | |
820 | ||
821 | -static void disk2memdqb(struct mem_dqblk *m, struct v2_disk_dqblk *d) | |
822 | +static void v2_disk2memdqb(struct dquot *dquot, void *dp) | |
823 | { | |
824 | + struct v2_disk_dqblk *d = dp, empty; | |
825 | + struct mem_dqblk *m = &dquot->dq_dqb; | |
826 | + | |
827 | m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit); | |
828 | m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit); | |
829 | m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes); | |
830 | @@ -122,10 +138,20 @@ static void disk2memdqb(struct mem_dqblk | |
831 | m->dqb_bsoftlimit = v2_qbtos(le32_to_cpu(d->dqb_bsoftlimit)); | |
832 | m->dqb_curspace = le64_to_cpu(d->dqb_curspace); | |
833 | m->dqb_btime = le64_to_cpu(d->dqb_btime); | |
834 | + /* We need to escape back all-zero structure */ | |
835 | + memset(&empty, 0, sizeof(struct v2_disk_dqblk)); | |
836 | + empty.dqb_itime = cpu_to_le64(1); | |
837 | + if (!memcmp(&empty, dp, sizeof(struct v2_disk_dqblk))) | |
838 | + m->dqb_itime = 0; | |
839 | } | |
840 | ||
841 | -static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id) | |
842 | +static void v2_mem2diskdqb(void *dp, struct dquot *dquot) | |
843 | { | |
844 | + struct v2_disk_dqblk *d = dp; | |
845 | + struct mem_dqblk *m = &dquot->dq_dqb; | |
846 | + struct qtree_mem_dqinfo *info = | |
847 | + &sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i; | |
848 | + | |
849 | d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit); | |
850 | d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit); | |
851 | d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes); | |
852 | @@ -134,553 +160,35 @@ static void mem2diskdqb(struct v2_disk_d | |
853 | d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit)); | |
854 | d->dqb_curspace = cpu_to_le64(m->dqb_curspace); | |
855 | d->dqb_btime = cpu_to_le64(m->dqb_btime); | |
856 | - d->dqb_id = cpu_to_le32(id); | |
857 | -} | |
858 | - | |
859 | -static dqbuf_t getdqbuf(void) | |
860 | -{ | |
861 | - dqbuf_t buf = kmalloc(V2_DQBLKSIZE, GFP_NOFS); | |
862 | - if (!buf) | |
863 | - printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n"); | |
864 | - return buf; | |
865 | -} | |
866 | - | |
867 | -static inline void freedqbuf(dqbuf_t buf) | |
868 | -{ | |
869 | - kfree(buf); | |
870 | -} | |
871 | - | |
872 | -static inline ssize_t read_blk(struct super_block *sb, int type, uint blk, dqbuf_t buf) | |
873 | -{ | |
874 | - memset(buf, 0, V2_DQBLKSIZE); | |
875 | - return sb->s_op->quota_read(sb, type, (char *)buf, | |
876 | - V2_DQBLKSIZE, blk << V2_DQBLKSIZE_BITS); | |
877 | -} | |
878 | - | |
879 | -static inline ssize_t write_blk(struct super_block *sb, int type, uint blk, dqbuf_t buf) | |
880 | -{ | |
881 | - return sb->s_op->quota_write(sb, type, (char *)buf, | |
882 | - V2_DQBLKSIZE, blk << V2_DQBLKSIZE_BITS); | |
883 | -} | |
884 | - | |
885 | -/* Remove empty block from list and return it */ | |
886 | -static int get_free_dqblk(struct super_block *sb, int type) | |
887 | -{ | |
888 | - dqbuf_t buf = getdqbuf(); | |
889 | - struct mem_dqinfo *info = sb_dqinfo(sb, type); | |
890 | - struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; | |
891 | - int ret, blk; | |
892 | - | |
893 | - if (!buf) | |
894 | - return -ENOMEM; | |
895 | - if (info->u.v2_i.dqi_free_blk) { | |
896 | - blk = info->u.v2_i.dqi_free_blk; | |
897 | - if ((ret = read_blk(sb, type, blk, buf)) < 0) | |
898 | - goto out_buf; | |
899 | - info->u.v2_i.dqi_free_blk = le32_to_cpu(dh->dqdh_next_free); | |
900 | - } | |
901 | - else { | |
902 | - memset(buf, 0, V2_DQBLKSIZE); | |
903 | - /* Assure block allocation... */ | |
904 | - if ((ret = write_blk(sb, type, info->u.v2_i.dqi_blocks, buf)) < 0) | |
905 | - goto out_buf; | |
906 | - blk = info->u.v2_i.dqi_blocks++; | |
907 | - } | |
908 | - mark_info_dirty(sb, type); | |
909 | - ret = blk; | |
910 | -out_buf: | |
911 | - freedqbuf(buf); | |
912 | - return ret; | |
913 | + d->dqb_id = cpu_to_le32(dquot->dq_id); | |
914 | + if (qtree_entry_unused(info, dp)) | |
915 | + d->dqb_itime = cpu_to_le64(1); | |
916 | } | |
917 | ||
918 | -/* Insert empty block to the list */ | |
919 | -static int put_free_dqblk(struct super_block *sb, int type, dqbuf_t buf, uint blk) | |
920 | +static int v2_is_id(void *dp, struct dquot *dquot) | |
921 | { | |
922 | - struct mem_dqinfo *info = sb_dqinfo(sb, type); | |
923 | - struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; | |
924 | - int err; | |
925 | - | |
926 | - dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_blk); | |
927 | - dh->dqdh_prev_free = cpu_to_le32(0); | |
928 | - dh->dqdh_entries = cpu_to_le16(0); | |
929 | - info->u.v2_i.dqi_free_blk = blk; | |
930 | - mark_info_dirty(sb, type); | |
931 | - /* Some strange block. We had better leave it... */ | |
932 | - if ((err = write_blk(sb, type, blk, buf)) < 0) | |
933 | - return err; | |
934 | - return 0; | |
935 | -} | |
936 | + struct v2_disk_dqblk *d = dp; | |
937 | + struct qtree_mem_dqinfo *info = | |
938 | + &sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i; | |
939 | ||
940 | -/* Remove given block from the list of blocks with free entries */ | |
941 | -static int remove_free_dqentry(struct super_block *sb, int type, dqbuf_t buf, uint blk) | |
942 | -{ | |
943 | - dqbuf_t tmpbuf = getdqbuf(); | |
944 | - struct mem_dqinfo *info = sb_dqinfo(sb, type); | |
945 | - struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; | |
946 | - uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free); | |
947 | - int err; | |
948 | - | |
949 | - if (!tmpbuf) | |
950 | - return -ENOMEM; | |
951 | - if (nextblk) { | |
952 | - if ((err = read_blk(sb, type, nextblk, tmpbuf)) < 0) | |
953 | - goto out_buf; | |
954 | - ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free; | |
955 | - if ((err = write_blk(sb, type, nextblk, tmpbuf)) < 0) | |
956 | - goto out_buf; | |
957 | - } | |
958 | - if (prevblk) { | |
959 | - if ((err = read_blk(sb, type, prevblk, tmpbuf)) < 0) | |
960 | - goto out_buf; | |
961 | - ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free; | |
962 | - if ((err = write_blk(sb, type, prevblk, tmpbuf)) < 0) | |
963 | - goto out_buf; | |
964 | - } | |
965 | - else { | |
966 | - info->u.v2_i.dqi_free_entry = nextblk; | |
967 | - mark_info_dirty(sb, type); | |
968 | - } | |
969 | - freedqbuf(tmpbuf); | |
970 | - dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0); | |
971 | - /* No matter whether write succeeds block is out of list */ | |
972 | - if (write_blk(sb, type, blk, buf) < 0) | |
973 | - printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk); | |
974 | - return 0; | |
975 | -out_buf: | |
976 | - freedqbuf(tmpbuf); | |
977 | - return err; | |
978 | -} | |
979 | - | |
980 | -/* Insert given block to the beginning of list with free entries */ | |
981 | -static int insert_free_dqentry(struct super_block *sb, int type, dqbuf_t buf, uint blk) | |
982 | -{ | |
983 | - dqbuf_t tmpbuf = getdqbuf(); | |
984 | - struct mem_dqinfo *info = sb_dqinfo(sb, type); | |
985 | - struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; | |
986 | - int err; | |
987 | - | |
988 | - if (!tmpbuf) | |
989 | - return -ENOMEM; | |
990 | - dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_entry); | |
991 | - dh->dqdh_prev_free = cpu_to_le32(0); | |
992 | - if ((err = write_blk(sb, type, blk, buf)) < 0) | |
993 | - goto out_buf; | |
994 | - if (info->u.v2_i.dqi_free_entry) { | |
995 | - if ((err = read_blk(sb, type, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0) | |
996 | - goto out_buf; | |
997 | - ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk); | |
998 | - if ((err = write_blk(sb, type, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0) | |
999 | - goto out_buf; | |
1000 | - } | |
1001 | - freedqbuf(tmpbuf); | |
1002 | - info->u.v2_i.dqi_free_entry = blk; | |
1003 | - mark_info_dirty(sb, type); | |
1004 | - return 0; | |
1005 | -out_buf: | |
1006 | - freedqbuf(tmpbuf); | |
1007 | - return err; | |
1008 | -} | |
1009 | - | |
1010 | -/* Find space for dquot */ | |
1011 | -static uint find_free_dqentry(struct dquot *dquot, int *err) | |
1012 | -{ | |
1013 | - struct super_block *sb = dquot->dq_sb; | |
1014 | - struct mem_dqinfo *info = sb_dqopt(sb)->info+dquot->dq_type; | |
1015 | - uint blk, i; | |
1016 | - struct v2_disk_dqdbheader *dh; | |
1017 | - struct v2_disk_dqblk *ddquot; | |
1018 | - struct v2_disk_dqblk fakedquot; | |
1019 | - dqbuf_t buf; | |
1020 | - | |
1021 | - *err = 0; | |
1022 | - if (!(buf = getdqbuf())) { | |
1023 | - *err = -ENOMEM; | |
1024 | + if (qtree_entry_unused(info, dp)) | |
1025 | return 0; | |
1026 | - } | |
1027 | - dh = (struct v2_disk_dqdbheader *)buf; | |
1028 | - ddquot = GETENTRIES(buf); | |
1029 | - if (info->u.v2_i.dqi_free_entry) { | |
1030 | - blk = info->u.v2_i.dqi_free_entry; | |
1031 | - if ((*err = read_blk(sb, dquot->dq_type, blk, buf)) < 0) | |
1032 | - goto out_buf; | |
1033 | - } | |
1034 | - else { | |
1035 | - blk = get_free_dqblk(sb, dquot->dq_type); | |
1036 | - if ((int)blk < 0) { | |
1037 | - *err = blk; | |
1038 | - freedqbuf(buf); | |
1039 | - return 0; | |
1040 | - } | |
1041 | - memset(buf, 0, V2_DQBLKSIZE); | |
1042 | - /* This is enough as block is already zeroed and entry list is empty... */ | |
1043 | - info->u.v2_i.dqi_free_entry = blk; | |
1044 | - mark_info_dirty(sb, dquot->dq_type); | |
1045 | - } | |
1046 | - if (le16_to_cpu(dh->dqdh_entries)+1 >= V2_DQSTRINBLK) /* Block will be full? */ | |
1047 | - if ((*err = remove_free_dqentry(sb, dquot->dq_type, buf, blk)) < 0) { | |
1048 | - printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk); | |
1049 | - goto out_buf; | |
1050 | - } | |
1051 | - le16_add_cpu(&dh->dqdh_entries, 1); | |
1052 | - memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk)); | |
1053 | - /* Find free structure in block */ | |
1054 | - for (i = 0; i < V2_DQSTRINBLK && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)); i++); | |
1055 | -#ifdef __QUOTA_V2_PARANOIA | |
1056 | - if (i == V2_DQSTRINBLK) { | |
1057 | - printk(KERN_ERR "VFS: find_free_dqentry(): Data block full but it shouldn't.\n"); | |
1058 | - *err = -EIO; | |
1059 | - goto out_buf; | |
1060 | - } | |
1061 | -#endif | |
1062 | - if ((*err = write_blk(sb, dquot->dq_type, blk, buf)) < 0) { | |
1063 | - printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota data block %u.\n", blk); | |
1064 | - goto out_buf; | |
1065 | - } | |
1066 | - dquot->dq_off = (blk<<V2_DQBLKSIZE_BITS)+sizeof(struct v2_disk_dqdbheader)+i*sizeof(struct v2_disk_dqblk); | |
1067 | - freedqbuf(buf); | |
1068 | - return blk; | |
1069 | -out_buf: | |
1070 | - freedqbuf(buf); | |
1071 | - return 0; | |
1072 | -} | |
1073 | - | |
1074 | -/* Insert reference to structure into the trie */ | |
1075 | -static int do_insert_tree(struct dquot *dquot, uint *treeblk, int depth) | |
1076 | -{ | |
1077 | - struct super_block *sb = dquot->dq_sb; | |
1078 | - dqbuf_t buf; | |
1079 | - int ret = 0, newson = 0, newact = 0; | |
1080 | - __le32 *ref; | |
1081 | - uint newblk; | |
1082 | - | |
1083 | - if (!(buf = getdqbuf())) | |
1084 | - return -ENOMEM; | |
1085 | - if (!*treeblk) { | |
1086 | - ret = get_free_dqblk(sb, dquot->dq_type); | |
1087 | - if (ret < 0) | |
1088 | - goto out_buf; | |
1089 | - *treeblk = ret; | |
1090 | - memset(buf, 0, V2_DQBLKSIZE); | |
1091 | - newact = 1; | |
1092 | - } | |
1093 | - else { | |
1094 | - if ((ret = read_blk(sb, dquot->dq_type, *treeblk, buf)) < 0) { | |
1095 | - printk(KERN_ERR "VFS: Can't read tree quota block %u.\n", *treeblk); | |
1096 | - goto out_buf; | |
1097 | - } | |
1098 | - } | |
1099 | - ref = (__le32 *)buf; | |
1100 | - newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]); | |
1101 | - if (!newblk) | |
1102 | - newson = 1; | |
1103 | - if (depth == V2_DQTREEDEPTH-1) { | |
1104 | -#ifdef __QUOTA_V2_PARANOIA | |
1105 | - if (newblk) { | |
1106 | - printk(KERN_ERR "VFS: Inserting already present quota entry (block %u).\n", le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)])); | |
1107 | - ret = -EIO; | |
1108 | - goto out_buf; | |
1109 | - } | |
1110 | -#endif | |
1111 | - newblk = find_free_dqentry(dquot, &ret); | |
1112 | - } | |
1113 | - else | |
1114 | - ret = do_insert_tree(dquot, &newblk, depth+1); | |
1115 | - if (newson && ret >= 0) { | |
1116 | - ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk); | |
1117 | - ret = write_blk(sb, dquot->dq_type, *treeblk, buf); | |
1118 | - } | |
1119 | - else if (newact && ret < 0) | |
1120 | - put_free_dqblk(sb, dquot->dq_type, buf, *treeblk); | |
1121 | -out_buf: | |
1122 | - freedqbuf(buf); | |
1123 | - return ret; | |
1124 | + return le32_to_cpu(d->dqb_id) == dquot->dq_id; | |
1125 | } | |
1126 | ||
1127 | -/* Wrapper for inserting quota structure into tree */ | |
1128 | -static inline int dq_insert_tree(struct dquot *dquot) | |
1129 | +static int v2_read_dquot(struct dquot *dquot) | |
1130 | { | |
1131 | - int tmp = V2_DQTREEOFF; | |
1132 | - return do_insert_tree(dquot, &tmp, 0); | |
1133 | + return qtree_read_dquot(&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot); | |
1134 | } | |
1135 | ||
1136 | -/* | |
1137 | - * We don't have to be afraid of deadlocks as we never have quotas on quota files... | |
1138 | - */ | |
1139 | static int v2_write_dquot(struct dquot *dquot) | |
1140 | { | |
1141 | - int type = dquot->dq_type; | |
1142 | - ssize_t ret; | |
1143 | - struct v2_disk_dqblk ddquot, empty; | |
1144 | - | |
1145 | - /* dq_off is guarded by dqio_mutex */ | |
1146 | - if (!dquot->dq_off) | |
1147 | - if ((ret = dq_insert_tree(dquot)) < 0) { | |
1148 | - printk(KERN_ERR "VFS: Error %zd occurred while creating quota.\n", ret); | |
1149 | - return ret; | |
1150 | - } | |
1151 | - spin_lock(&dq_data_lock); | |
1152 | - mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id); | |
1153 | - /* Argh... We may need to write structure full of zeroes but that would be | |
1154 | - * treated as an empty place by the rest of the code. Format change would | |
1155 | - * be definitely cleaner but the problems probably are not worth it */ | |
1156 | - memset(&empty, 0, sizeof(struct v2_disk_dqblk)); | |
1157 | - if (!memcmp(&empty, &ddquot, sizeof(struct v2_disk_dqblk))) | |
1158 | - ddquot.dqb_itime = cpu_to_le64(1); | |
1159 | - spin_unlock(&dq_data_lock); | |
1160 | - ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type, | |
1161 | - (char *)&ddquot, sizeof(struct v2_disk_dqblk), dquot->dq_off); | |
1162 | - if (ret != sizeof(struct v2_disk_dqblk)) { | |
1163 | - printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", dquot->dq_sb->s_id); | |
1164 | - if (ret >= 0) | |
1165 | - ret = -ENOSPC; | |
1166 | - } | |
1167 | - else | |
1168 | - ret = 0; | |
1169 | - dqstats.writes++; | |
1170 | - | |
1171 | - return ret; | |
1172 | -} | |
1173 | - | |
1174 | -/* Free dquot entry in data block */ | |
1175 | -static int free_dqentry(struct dquot *dquot, uint blk) | |
1176 | -{ | |
1177 | - struct super_block *sb = dquot->dq_sb; | |
1178 | - int type = dquot->dq_type; | |
1179 | - struct v2_disk_dqdbheader *dh; | |
1180 | - dqbuf_t buf = getdqbuf(); | |
1181 | - int ret = 0; | |
1182 | - | |
1183 | - if (!buf) | |
1184 | - return -ENOMEM; | |
1185 | - if (dquot->dq_off >> V2_DQBLKSIZE_BITS != blk) { | |
1186 | - printk(KERN_ERR "VFS: Quota structure has offset to other " | |
1187 | - "block (%u) than it should (%u).\n", blk, | |
1188 | - (uint)(dquot->dq_off >> V2_DQBLKSIZE_BITS)); | |
1189 | - goto out_buf; | |
1190 | - } | |
1191 | - if ((ret = read_blk(sb, type, blk, buf)) < 0) { | |
1192 | - printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk); | |
1193 | - goto out_buf; | |
1194 | - } | |
1195 | - dh = (struct v2_disk_dqdbheader *)buf; | |
1196 | - le16_add_cpu(&dh->dqdh_entries, -1); | |
1197 | - if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */ | |
1198 | - if ((ret = remove_free_dqentry(sb, type, buf, blk)) < 0 || | |
1199 | - (ret = put_free_dqblk(sb, type, buf, blk)) < 0) { | |
1200 | - printk(KERN_ERR "VFS: Can't move quota data block (%u) " | |
1201 | - "to free list.\n", blk); | |
1202 | - goto out_buf; | |
1203 | - } | |
1204 | - } | |
1205 | - else { | |
1206 | - memset(buf+(dquot->dq_off & ((1 << V2_DQBLKSIZE_BITS)-1)), 0, | |
1207 | - sizeof(struct v2_disk_dqblk)); | |
1208 | - if (le16_to_cpu(dh->dqdh_entries) == V2_DQSTRINBLK-1) { | |
1209 | - /* Insert will write block itself */ | |
1210 | - if ((ret = insert_free_dqentry(sb, type, buf, blk)) < 0) { | |
1211 | - printk(KERN_ERR "VFS: Can't insert quota data block (%u) to free entry list.\n", blk); | |
1212 | - goto out_buf; | |
1213 | - } | |
1214 | - } | |
1215 | - else | |
1216 | - if ((ret = write_blk(sb, type, blk, buf)) < 0) { | |
1217 | - printk(KERN_ERR "VFS: Can't write quota data " | |
1218 | - "block %u\n", blk); | |
1219 | - goto out_buf; | |
1220 | - } | |
1221 | - } | |
1222 | - dquot->dq_off = 0; /* Quota is now unattached */ | |
1223 | -out_buf: | |
1224 | - freedqbuf(buf); | |
1225 | - return ret; | |
1226 | -} | |
1227 | - | |
1228 | -/* Remove reference to dquot from tree */ | |
1229 | -static int remove_tree(struct dquot *dquot, uint *blk, int depth) | |
1230 | -{ | |
1231 | - struct super_block *sb = dquot->dq_sb; | |
1232 | - int type = dquot->dq_type; | |
1233 | - dqbuf_t buf = getdqbuf(); | |
1234 | - int ret = 0; | |
1235 | - uint newblk; | |
1236 | - __le32 *ref = (__le32 *)buf; | |
1237 | - | |
1238 | - if (!buf) | |
1239 | - return -ENOMEM; | |
1240 | - if ((ret = read_blk(sb, type, *blk, buf)) < 0) { | |
1241 | - printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk); | |
1242 | - goto out_buf; | |
1243 | - } | |
1244 | - newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]); | |
1245 | - if (depth == V2_DQTREEDEPTH-1) { | |
1246 | - ret = free_dqentry(dquot, newblk); | |
1247 | - newblk = 0; | |
1248 | - } | |
1249 | - else | |
1250 | - ret = remove_tree(dquot, &newblk, depth+1); | |
1251 | - if (ret >= 0 && !newblk) { | |
1252 | - int i; | |
1253 | - ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0); | |
1254 | - for (i = 0; i < V2_DQBLKSIZE && !buf[i]; i++); /* Block got empty? */ | |
1255 | - /* Don't put the root block into the free block list */ | |
1256 | - if (i == V2_DQBLKSIZE && *blk != V2_DQTREEOFF) { | |
1257 | - put_free_dqblk(sb, type, buf, *blk); | |
1258 | - *blk = 0; | |
1259 | - } | |
1260 | - else | |
1261 | - if ((ret = write_blk(sb, type, *blk, buf)) < 0) | |
1262 | - printk(KERN_ERR "VFS: Can't write quota tree " | |
1263 | - "block %u.\n", *blk); | |
1264 | - } | |
1265 | -out_buf: | |
1266 | - freedqbuf(buf); | |
1267 | - return ret; | |
1268 | + return qtree_write_dquot(&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot); | |
1269 | } | |
1270 | ||
1271 | -/* Delete dquot from tree */ | |
1272 | -static int v2_delete_dquot(struct dquot *dquot) | |
1273 | -{ | |
1274 | - uint tmp = V2_DQTREEOFF; | |
1275 | - | |
1276 | - if (!dquot->dq_off) /* Even not allocated? */ | |
1277 | - return 0; | |
1278 | - return remove_tree(dquot, &tmp, 0); | |
1279 | -} | |
1280 | - | |
1281 | -/* Find entry in block */ | |
1282 | -static loff_t find_block_dqentry(struct dquot *dquot, uint blk) | |
1283 | -{ | |
1284 | - dqbuf_t buf = getdqbuf(); | |
1285 | - loff_t ret = 0; | |
1286 | - int i; | |
1287 | - struct v2_disk_dqblk *ddquot = GETENTRIES(buf); | |
1288 | - | |
1289 | - if (!buf) | |
1290 | - return -ENOMEM; | |
1291 | - if ((ret = read_blk(dquot->dq_sb, dquot->dq_type, blk, buf)) < 0) { | |
1292 | - printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); | |
1293 | - goto out_buf; | |
1294 | - } | |
1295 | - if (dquot->dq_id) | |
1296 | - for (i = 0; i < V2_DQSTRINBLK && | |
1297 | - le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++); | |
1298 | - else { /* ID 0 as a bit more complicated searching... */ | |
1299 | - struct v2_disk_dqblk fakedquot; | |
1300 | - | |
1301 | - memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk)); | |
1302 | - for (i = 0; i < V2_DQSTRINBLK; i++) | |
1303 | - if (!le32_to_cpu(ddquot[i].dqb_id) && | |
1304 | - memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk))) | |
1305 | - break; | |
1306 | - } | |
1307 | - if (i == V2_DQSTRINBLK) { | |
1308 | - printk(KERN_ERR "VFS: Quota for id %u referenced " | |
1309 | - "but not present.\n", dquot->dq_id); | |
1310 | - ret = -EIO; | |
1311 | - goto out_buf; | |
1312 | - } | |
1313 | - else | |
1314 | - ret = (blk << V2_DQBLKSIZE_BITS) + sizeof(struct | |
1315 | - v2_disk_dqdbheader) + i * sizeof(struct v2_disk_dqblk); | |
1316 | -out_buf: | |
1317 | - freedqbuf(buf); | |
1318 | - return ret; | |
1319 | -} | |
1320 | - | |
1321 | -/* Find entry for given id in the tree */ | |
1322 | -static loff_t find_tree_dqentry(struct dquot *dquot, uint blk, int depth) | |
1323 | -{ | |
1324 | - dqbuf_t buf = getdqbuf(); | |
1325 | - loff_t ret = 0; | |
1326 | - __le32 *ref = (__le32 *)buf; | |
1327 | - | |
1328 | - if (!buf) | |
1329 | - return -ENOMEM; | |
1330 | - if ((ret = read_blk(dquot->dq_sb, dquot->dq_type, blk, buf)) < 0) { | |
1331 | - printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); | |
1332 | - goto out_buf; | |
1333 | - } | |
1334 | - ret = 0; | |
1335 | - blk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]); | |
1336 | - if (!blk) /* No reference? */ | |
1337 | - goto out_buf; | |
1338 | - if (depth < V2_DQTREEDEPTH-1) | |
1339 | - ret = find_tree_dqentry(dquot, blk, depth+1); | |
1340 | - else | |
1341 | - ret = find_block_dqentry(dquot, blk); | |
1342 | -out_buf: | |
1343 | - freedqbuf(buf); | |
1344 | - return ret; | |
1345 | -} | |
1346 | - | |
1347 | -/* Find entry for given id in the tree - wrapper function */ | |
1348 | -static inline loff_t find_dqentry(struct dquot *dquot) | |
1349 | -{ | |
1350 | - return find_tree_dqentry(dquot, V2_DQTREEOFF, 0); | |
1351 | -} | |
1352 | - | |
1353 | -static int v2_read_dquot(struct dquot *dquot) | |
1354 | -{ | |
1355 | - int type = dquot->dq_type; | |
1356 | - loff_t offset; | |
1357 | - struct v2_disk_dqblk ddquot, empty; | |
1358 | - int ret = 0; | |
1359 | - | |
1360 | -#ifdef __QUOTA_V2_PARANOIA | |
1361 | - /* Invalidated quota? */ | |
1362 | - if (!dquot->dq_sb || !sb_dqopt(dquot->dq_sb)->files[type]) { | |
1363 | - printk(KERN_ERR "VFS: Quota invalidated while reading!\n"); | |
1364 | - return -EIO; | |
1365 | - } | |
1366 | -#endif | |
1367 | - offset = find_dqentry(dquot); | |
1368 | - if (offset <= 0) { /* Entry not present? */ | |
1369 | - if (offset < 0) | |
1370 | - printk(KERN_ERR "VFS: Can't read quota " | |
1371 | - "structure for id %u.\n", dquot->dq_id); | |
1372 | - dquot->dq_off = 0; | |
1373 | - set_bit(DQ_FAKE_B, &dquot->dq_flags); | |
1374 | - memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); | |
1375 | - ret = offset; | |
1376 | - } | |
1377 | - else { | |
1378 | - dquot->dq_off = offset; | |
1379 | - if ((ret = dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, | |
1380 | - (char *)&ddquot, sizeof(struct v2_disk_dqblk), offset)) | |
1381 | - != sizeof(struct v2_disk_dqblk)) { | |
1382 | - if (ret >= 0) | |
1383 | - ret = -EIO; | |
1384 | - printk(KERN_ERR "VFS: Error while reading quota " | |
1385 | - "structure for id %u.\n", dquot->dq_id); | |
1386 | - memset(&ddquot, 0, sizeof(struct v2_disk_dqblk)); | |
1387 | - } | |
1388 | - else { | |
1389 | - ret = 0; | |
1390 | - /* We need to escape back all-zero structure */ | |
1391 | - memset(&empty, 0, sizeof(struct v2_disk_dqblk)); | |
1392 | - empty.dqb_itime = cpu_to_le64(1); | |
1393 | - if (!memcmp(&empty, &ddquot, sizeof(struct v2_disk_dqblk))) | |
1394 | - ddquot.dqb_itime = 0; | |
1395 | - } | |
1396 | - disk2memdqb(&dquot->dq_dqb, &ddquot); | |
1397 | - if (!dquot->dq_dqb.dqb_bhardlimit && | |
1398 | - !dquot->dq_dqb.dqb_bsoftlimit && | |
1399 | - !dquot->dq_dqb.dqb_ihardlimit && | |
1400 | - !dquot->dq_dqb.dqb_isoftlimit) | |
1401 | - set_bit(DQ_FAKE_B, &dquot->dq_flags); | |
1402 | - } | |
1403 | - dqstats.reads++; | |
1404 | - | |
1405 | - return ret; | |
1406 | -} | |
1407 | - | |
1408 | -/* Check whether dquot should not be deleted. We know we are | |
1409 | - * the only one operating on dquot (thanks to dq_lock) */ | |
1410 | static int v2_release_dquot(struct dquot *dquot) | |
1411 | { | |
1412 | - if (test_bit(DQ_FAKE_B, &dquot->dq_flags) && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) | |
1413 | - return v2_delete_dquot(dquot); | |
1414 | - return 0; | |
1415 | + return qtree_release_dquot(&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot); | |
1416 | } | |
1417 | ||
1418 | static struct quota_format_ops v2_format_ops = { | |
1419 | --- a/fs/quotaio_v2.h | |
1420 | +++ b/fs/quotaio_v2.h | |
1421 | @@ -21,6 +21,12 @@ | |
1422 | 0 /* GRPQUOTA */\ | |
1423 | } | |
1424 | ||
1425 | +/* First generic header */ | |
1426 | +struct v2_disk_dqheader { | |
1427 | + __le32 dqh_magic; /* Magic number identifying file */ | |
1428 | + __le32 dqh_version; /* File version */ | |
1429 | +}; | |
1430 | + | |
1431 | /* | |
1432 | * The following structure defines the format of the disk quota file | |
1433 | * (as it appears on disk) - the file is a radix tree whose leaves point | |
1434 | @@ -38,15 +44,6 @@ struct v2_disk_dqblk { | |
1435 | __le64 dqb_itime; /* time limit for excessive inode use */ | |
1436 | }; | |
1437 | ||
1438 | -/* | |
1439 | - * Here are header structures as written on disk and their in-memory copies | |
1440 | - */ | |
1441 | -/* First generic header */ | |
1442 | -struct v2_disk_dqheader { | |
1443 | - __le32 dqh_magic; /* Magic number identifying file */ | |
1444 | - __le32 dqh_version; /* File version */ | |
1445 | -}; | |
1446 | - | |
1447 | /* Header with type and version specific information */ | |
1448 | struct v2_disk_dqinfo { | |
1449 | __le32 dqi_bgrace; /* Time before block soft limit becomes hard limit */ | |
1450 | @@ -57,23 +54,7 @@ struct v2_disk_dqinfo { | |
1451 | __le32 dqi_free_entry; /* Number of block with at least one free entry */ | |
1452 | }; | |
1453 | ||
1454 | -/* | |
1455 | - * Structure of header of block with quota structures. It is padded to 16 bytes so | |
1456 | - * there will be space for exactly 21 quota-entries in a block | |
1457 | - */ | |
1458 | -struct v2_disk_dqdbheader { | |
1459 | - __le32 dqdh_next_free; /* Number of next block with free entry */ | |
1460 | - __le32 dqdh_prev_free; /* Number of previous block with free entry */ | |
1461 | - __le16 dqdh_entries; /* Number of valid entries in block */ | |
1462 | - __le16 dqdh_pad1; | |
1463 | - __le32 dqdh_pad2; | |
1464 | -}; | |
1465 | - | |
1466 | #define V2_DQINFOOFF sizeof(struct v2_disk_dqheader) /* Offset of info header in file */ | |
1467 | -#define V2_DQBLKSIZE_BITS 10 | |
1468 | -#define V2_DQBLKSIZE (1 << V2_DQBLKSIZE_BITS) /* Size of block with quota structures */ | |
1469 | -#define V2_DQTREEOFF 1 /* Offset of tree in file in blocks */ | |
1470 | -#define V2_DQTREEDEPTH 4 /* Depth of quota tree */ | |
1471 | -#define V2_DQSTRINBLK ((V2_DQBLKSIZE - sizeof(struct v2_disk_dqdbheader)) / sizeof(struct v2_disk_dqblk)) /* Number of entries in one blocks */ | |
1472 | +#define V2_DQBLKSIZE_BITS 10 /* Size of leaf block in tree */ | |
1473 | ||
1474 | #endif /* _LINUX_QUOTAIO_V2_H */ | |
1475 | --- /dev/null | |
1476 | +++ b/include/linux/dqblk_qtree.h | |
1477 | @@ -0,0 +1,56 @@ | |
1478 | +/* | |
1479 | + * Definitions of structures and functions for quota formats using trie | |
1480 | + */ | |
1481 | + | |
1482 | +#ifndef _LINUX_DQBLK_QTREE_H | |
1483 | +#define _LINUX_DQBLK_QTREE_H | |
1484 | + | |
1485 | +#include <linux/types.h> | |
1486 | + | |
1487 | +/* Numbers of blocks needed for updates - we count with the smallest | |
1488 | + * possible block size (1024) */ | |
1489 | +#define QTREE_INIT_ALLOC 4 | |
1490 | +#define QTREE_INIT_REWRITE 2 | |
1491 | +#define QTREE_DEL_ALLOC 0 | |
1492 | +#define QTREE_DEL_REWRITE 6 | |
1493 | + | |
1494 | +struct dquot; | |
1495 | + | |
1496 | +/* Operations */ | |
1497 | +struct qtree_fmt_operations { | |
1498 | + void (*mem2disk_dqblk)(void *disk, struct dquot *dquot); /* Convert given entry from in memory format to disk one */ | |
1499 | + void (*disk2mem_dqblk)(struct dquot *dquot, void *disk); /* Convert given entry from disk format to in memory one */ | |
1500 | + int (*is_id)(void *disk, struct dquot *dquot); /* Is this structure for given id? */ | |
1501 | +}; | |
1502 | + | |
1503 | +/* Inmemory copy of version specific information */ | |
1504 | +struct qtree_mem_dqinfo { | |
1505 | + struct super_block *dqi_sb; /* Sb quota is on */ | |
1506 | + int dqi_type; /* Quota type */ | |
1507 | + unsigned int dqi_blocks; /* # of blocks in quota file */ | |
1508 | + unsigned int dqi_free_blk; /* First block in list of free blocks */ | |
1509 | + unsigned int dqi_free_entry; /* First block with free entry */ | |
1510 | + unsigned int dqi_blocksize_bits; /* Block size of quota file */ | |
1511 | + unsigned int dqi_entry_size; /* Size of quota entry in quota file */ | |
1512 | + unsigned int dqi_usable_bs; /* Space usable in block for quota data */ | |
1513 | + unsigned int dqi_qtree_depth; /* Precomputed depth of quota tree */ | |
1514 | + struct qtree_fmt_operations *dqi_ops; /* Operations for entry manipulation */ | |
1515 | +}; | |
1516 | + | |
1517 | +int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot); | |
1518 | +int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot); | |
1519 | +int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot); | |
1520 | +int qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot); | |
1521 | +int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk); | |
1522 | +static inline int qtree_depth(struct qtree_mem_dqinfo *info) | |
1523 | +{ | |
1524 | + unsigned int epb = info->dqi_usable_bs >> 2; | |
1525 | + unsigned long long entries = epb; | |
1526 | + int i; | |
1527 | + | |
1528 | + for (i = 1; entries < (1ULL << 32); i++) | |
1529 | + entries *= epb; | |
1530 | + return i; | |
1531 | +} | |
1532 | + | |
1533 | +#endif /* _LINUX_DQBLK_QTREE_H */ | |
1534 | --- a/include/linux/dqblk_v2.h | |
1535 | +++ b/include/linux/dqblk_v2.h | |
1536 | @@ -1,26 +1,23 @@ | |
1537 | /* | |
1538 | - * Definitions of structures for vfsv0 quota format | |
1539 | + * Definitions for vfsv0 quota format | |
1540 | */ | |
1541 | ||
1542 | #ifndef _LINUX_DQBLK_V2_H | |
1543 | #define _LINUX_DQBLK_V2_H | |
1544 | ||
1545 | -#include <linux/types.h> | |
1546 | +#include <linux/dqblk_qtree.h> | |
1547 | ||
1548 | -/* id numbers of quota format */ | |
1549 | +/* Id number of quota format */ | |
1550 | #define QFMT_VFS_V0 2 | |
1551 | ||
1552 | /* Numbers of blocks needed for updates */ | |
1553 | -#define V2_INIT_ALLOC 4 | |
1554 | -#define V2_INIT_REWRITE 2 | |
1555 | -#define V2_DEL_ALLOC 0 | |
1556 | -#define V2_DEL_REWRITE 6 | |
1557 | +#define V2_INIT_ALLOC QTREE_INIT_ALLOC | |
1558 | +#define V2_INIT_REWRITE QTREE_INIT_REWRITE | |
1559 | +#define V2_DEL_ALLOC QTREE_DEL_ALLOC | |
1560 | +#define V2_DEL_REWRITE QTREE_DEL_REWRITE | |
1561 | ||
1562 | -/* Inmemory copy of version specific information */ | |
1563 | struct v2_mem_dqinfo { | |
1564 | - unsigned int dqi_blocks; | |
1565 | - unsigned int dqi_free_blk; | |
1566 | - unsigned int dqi_free_entry; | |
1567 | + struct qtree_mem_dqinfo i; | |
1568 | }; | |
1569 | ||
1570 | #endif /* _LINUX_DQBLK_V2_H */ |