]> bbs.cooldavid.org Git - net-next-2.6.git/blob - fs/quota/quota_tree.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[net-next-2.6.git] / fs / quota / quota_tree.c
1 /*
2  *      vfsv0 quota IO operations on file
3  */
4
5 #include <linux/errno.h>
6 #include <linux/fs.h>
7 #include <linux/mount.h>
8 #include <linux/dqblk_v2.h>
9 #include <linux/kernel.h>
10 #include <linux/init.h>
11 #include <linux/module.h>
12 #include <linux/slab.h>
13 #include <linux/quotaops.h>
14
15 #include <asm/byteorder.h>
16
17 #include "quota_tree.h"
18
19 MODULE_AUTHOR("Jan Kara");
20 MODULE_DESCRIPTION("Quota trie support");
21 MODULE_LICENSE("GPL");
22
23 #define __QUOTA_QT_PARANOIA
24
25 static int get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
26 {
27         unsigned int epb = info->dqi_usable_bs >> 2;
28
29         depth = info->dqi_qtree_depth - depth - 1;
30         while (depth--)
31                 id /= epb;
32         return id % epb;
33 }
34
35 /* Number of entries in one blocks */
36 static int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
37 {
38         return (info->dqi_usable_bs - sizeof(struct qt_disk_dqdbheader))
39                / info->dqi_entry_size;
40 }
41
42 static char *getdqbuf(size_t size)
43 {
44         char *buf = kmalloc(size, GFP_NOFS);
45         if (!buf)
46                 printk(KERN_WARNING
47                        "VFS: Not enough memory for quota buffers.\n");
48         return buf;
49 }
50
51 static ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf)
52 {
53         struct super_block *sb = info->dqi_sb;
54
55         memset(buf, 0, info->dqi_usable_bs);
56         return sb->s_op->quota_read(sb, info->dqi_type, buf,
57                info->dqi_usable_bs, blk << info->dqi_blocksize_bits);
58 }
59
60 static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf)
61 {
62         struct super_block *sb = info->dqi_sb;
63         ssize_t ret;
64
65         ret = sb->s_op->quota_write(sb, info->dqi_type, buf,
66                info->dqi_usable_bs, blk << info->dqi_blocksize_bits);
67         if (ret != info->dqi_usable_bs) {
68                 quota_error(sb, "dquota write failed");
69                 if (ret >= 0)
70                         ret = -EIO;
71         }
72         return ret;
73 }
74
75 /* Remove empty block from list and return it */
76 static int get_free_dqblk(struct qtree_mem_dqinfo *info)
77 {
78         char *buf = getdqbuf(info->dqi_usable_bs);
79         struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
80         int ret, blk;
81
82         if (!buf)
83                 return -ENOMEM;
84         if (info->dqi_free_blk) {
85                 blk = info->dqi_free_blk;
86                 ret = read_blk(info, blk, buf);
87                 if (ret < 0)
88                         goto out_buf;
89                 info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
90         }
91         else {
92                 memset(buf, 0, info->dqi_usable_bs);
93                 /* Assure block allocation... */
94                 ret = write_blk(info, info->dqi_blocks, buf);
95                 if (ret < 0)
96                         goto out_buf;
97                 blk = info->dqi_blocks++;
98         }
99         mark_info_dirty(info->dqi_sb, info->dqi_type);
100         ret = blk;
101 out_buf:
102         kfree(buf);
103         return ret;
104 }
105
106 /* Insert empty block to the list */
107 static int put_free_dqblk(struct qtree_mem_dqinfo *info, char *buf, uint blk)
108 {
109         struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
110         int err;
111
112         dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk);
113         dh->dqdh_prev_free = cpu_to_le32(0);
114         dh->dqdh_entries = cpu_to_le16(0);
115         err = write_blk(info, blk, buf);
116         if (err < 0)
117                 return err;
118         info->dqi_free_blk = blk;
119         mark_info_dirty(info->dqi_sb, info->dqi_type);
120         return 0;
121 }
122
123 /* Remove given block from the list of blocks with free entries */
124 static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
125                                uint blk)
126 {
127         char *tmpbuf = getdqbuf(info->dqi_usable_bs);
128         struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
129         uint nextblk = le32_to_cpu(dh->dqdh_next_free);
130         uint prevblk = le32_to_cpu(dh->dqdh_prev_free);
131         int err;
132
133         if (!tmpbuf)
134                 return -ENOMEM;
135         if (nextblk) {
136                 err = read_blk(info, nextblk, tmpbuf);
137                 if (err < 0)
138                         goto out_buf;
139                 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
140                                                         dh->dqdh_prev_free;
141                 err = write_blk(info, nextblk, tmpbuf);
142                 if (err < 0)
143                         goto out_buf;
144         }
145         if (prevblk) {
146                 err = read_blk(info, prevblk, tmpbuf);
147                 if (err < 0)
148                         goto out_buf;
149                 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
150                                                         dh->dqdh_next_free;
151                 err = write_blk(info, prevblk, tmpbuf);
152                 if (err < 0)
153                         goto out_buf;
154         } else {
155                 info->dqi_free_entry = nextblk;
156                 mark_info_dirty(info->dqi_sb, info->dqi_type);
157         }
158         kfree(tmpbuf);
159         dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
160         /* No matter whether write succeeds block is out of list */
161         if (write_blk(info, blk, buf) < 0)
162                 quota_error(info->dqi_sb, "Can't write block (%u) "
163                             "with free entries", blk);
164         return 0;
165 out_buf:
166         kfree(tmpbuf);
167         return err;
168 }
169
170 /* Insert given block to the beginning of list with free entries */
171 static int insert_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
172                                uint blk)
173 {
174         char *tmpbuf = getdqbuf(info->dqi_usable_bs);
175         struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
176         int err;
177
178         if (!tmpbuf)
179                 return -ENOMEM;
180         dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry);
181         dh->dqdh_prev_free = cpu_to_le32(0);
182         err = write_blk(info, blk, buf);
183         if (err < 0)
184                 goto out_buf;
185         if (info->dqi_free_entry) {
186                 err = read_blk(info, info->dqi_free_entry, tmpbuf);
187                 if (err < 0)
188                         goto out_buf;
189                 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
190                                                         cpu_to_le32(blk);
191                 err = write_blk(info, info->dqi_free_entry, tmpbuf);
192                 if (err < 0)
193                         goto out_buf;
194         }
195         kfree(tmpbuf);
196         info->dqi_free_entry = blk;
197         mark_info_dirty(info->dqi_sb, info->dqi_type);
198         return 0;
199 out_buf:
200         kfree(tmpbuf);
201         return err;
202 }
203
204 /* Is the entry in the block free? */
205 int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
206 {
207         int i;
208
209         for (i = 0; i < info->dqi_entry_size; i++)
210                 if (disk[i])
211                         return 0;
212         return 1;
213 }
214 EXPORT_SYMBOL(qtree_entry_unused);
215
216 /* Find space for dquot */
217 static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
218                               struct dquot *dquot, int *err)
219 {
220         uint blk, i;
221         struct qt_disk_dqdbheader *dh;
222         char *buf = getdqbuf(info->dqi_usable_bs);
223         char *ddquot;
224
225         *err = 0;
226         if (!buf) {
227                 *err = -ENOMEM;
228                 return 0;
229         }
230         dh = (struct qt_disk_dqdbheader *)buf;
231         if (info->dqi_free_entry) {
232                 blk = info->dqi_free_entry;
233                 *err = read_blk(info, blk, buf);
234                 if (*err < 0)
235                         goto out_buf;
236         } else {
237                 blk = get_free_dqblk(info);
238                 if ((int)blk < 0) {
239                         *err = blk;
240                         kfree(buf);
241                         return 0;
242                 }
243                 memset(buf, 0, info->dqi_usable_bs);
244                 /* This is enough as the block is already zeroed and the entry
245                  * list is empty... */
246                 info->dqi_free_entry = blk;
247                 mark_info_dirty(dquot->dq_sb, dquot->dq_type);
248         }
249         /* Block will be full? */
250         if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) {
251                 *err = remove_free_dqentry(info, buf, blk);
252                 if (*err < 0) {
253                         quota_error(dquot->dq_sb, "Can't remove block (%u) "
254                                     "from entry free list", blk);
255                         goto out_buf;
256                 }
257         }
258         le16_add_cpu(&dh->dqdh_entries, 1);
259         /* Find free structure in block */
260         ddquot = buf + sizeof(struct qt_disk_dqdbheader);
261         for (i = 0; i < qtree_dqstr_in_blk(info); i++) {
262                 if (qtree_entry_unused(info, ddquot))
263                         break;
264                 ddquot += info->dqi_entry_size;
265         }
266 #ifdef __QUOTA_QT_PARANOIA
267         if (i == qtree_dqstr_in_blk(info)) {
268                 quota_error(dquot->dq_sb, "Data block full but it shouldn't");
269                 *err = -EIO;
270                 goto out_buf;
271         }
272 #endif
273         *err = write_blk(info, blk, buf);
274         if (*err < 0) {
275                 quota_error(dquot->dq_sb, "Can't write quota data block %u",
276                             blk);
277                 goto out_buf;
278         }
279         dquot->dq_off = (blk << info->dqi_blocksize_bits) +
280                         sizeof(struct qt_disk_dqdbheader) +
281                         i * info->dqi_entry_size;
282         kfree(buf);
283         return blk;
284 out_buf:
285         kfree(buf);
286         return 0;
287 }
288
289 /* Insert reference to structure into the trie */
290 static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
291                           uint *treeblk, int depth)
292 {
293         char *buf = getdqbuf(info->dqi_usable_bs);
294         int ret = 0, newson = 0, newact = 0;
295         __le32 *ref;
296         uint newblk;
297
298         if (!buf)
299                 return -ENOMEM;
300         if (!*treeblk) {
301                 ret = get_free_dqblk(info);
302                 if (ret < 0)
303                         goto out_buf;
304                 *treeblk = ret;
305                 memset(buf, 0, info->dqi_usable_bs);
306                 newact = 1;
307         } else {
308                 ret = read_blk(info, *treeblk, buf);
309                 if (ret < 0) {
310                         quota_error(dquot->dq_sb, "Can't read tree quota "
311                                     "block %u", *treeblk);
312                         goto out_buf;
313                 }
314         }
315         ref = (__le32 *)buf;
316         newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
317         if (!newblk)
318                 newson = 1;
319         if (depth == info->dqi_qtree_depth - 1) {
320 #ifdef __QUOTA_QT_PARANOIA
321                 if (newblk) {
322                         quota_error(dquot->dq_sb, "Inserting already present "
323                                     "quota entry (block %u)",
324                                     le32_to_cpu(ref[get_index(info,
325                                                 dquot->dq_id, depth)]));
326                         ret = -EIO;
327                         goto out_buf;
328                 }
329 #endif
330                 newblk = find_free_dqentry(info, dquot, &ret);
331         } else {
332                 ret = do_insert_tree(info, dquot, &newblk, depth+1);
333         }
334         if (newson && ret >= 0) {
335                 ref[get_index(info, dquot->dq_id, depth)] =
336                                                         cpu_to_le32(newblk);
337                 ret = write_blk(info, *treeblk, buf);
338         } else if (newact && ret < 0) {
339                 put_free_dqblk(info, buf, *treeblk);
340         }
341 out_buf:
342         kfree(buf);
343         return ret;
344 }
345
346 /* Wrapper for inserting quota structure into tree */
347 static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
348                                  struct dquot *dquot)
349 {
350         int tmp = QT_TREEOFF;
351         return do_insert_tree(info, dquot, &tmp, 0);
352 }
353
354 /*
355  * We don't have to be afraid of deadlocks as we never have quotas on quota
356  * files...
357  */
358 int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
359 {
360         int type = dquot->dq_type;
361         struct super_block *sb = dquot->dq_sb;
362         ssize_t ret;
363         char *ddquot = getdqbuf(info->dqi_entry_size);
364
365         if (!ddquot)
366                 return -ENOMEM;
367
368         /* dq_off is guarded by dqio_mutex */
369         if (!dquot->dq_off) {
370                 ret = dq_insert_tree(info, dquot);
371                 if (ret < 0) {
372                         quota_error(sb, "Error %zd occurred while creating "
373                                     "quota", ret);
374                         kfree(ddquot);
375                         return ret;
376                 }
377         }
378         spin_lock(&dq_data_lock);
379         info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
380         spin_unlock(&dq_data_lock);
381         ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size,
382                                     dquot->dq_off);
383         if (ret != info->dqi_entry_size) {
384                 quota_error(sb, "dquota write failed");
385                 if (ret >= 0)
386                         ret = -ENOSPC;
387         } else {
388                 ret = 0;
389         }
390         dqstats_inc(DQST_WRITES);
391         kfree(ddquot);
392
393         return ret;
394 }
395 EXPORT_SYMBOL(qtree_write_dquot);
396
397 /* Free dquot entry in data block */
398 static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
399                         uint blk)
400 {
401         struct qt_disk_dqdbheader *dh;
402         char *buf = getdqbuf(info->dqi_usable_bs);
403         int ret = 0;
404
405         if (!buf)
406                 return -ENOMEM;
407         if (dquot->dq_off >> info->dqi_blocksize_bits != blk) {
408                 quota_error(dquot->dq_sb, "Quota structure has offset to "
409                         "other block (%u) than it should (%u)", blk,
410                         (uint)(dquot->dq_off >> info->dqi_blocksize_bits));
411                 goto out_buf;
412         }
413         ret = read_blk(info, blk, buf);
414         if (ret < 0) {
415                 quota_error(dquot->dq_sb, "Can't read quota data block %u",
416                             blk);
417                 goto out_buf;
418         }
419         dh = (struct qt_disk_dqdbheader *)buf;
420         le16_add_cpu(&dh->dqdh_entries, -1);
421         if (!le16_to_cpu(dh->dqdh_entries)) {   /* Block got free? */
422                 ret = remove_free_dqentry(info, buf, blk);
423                 if (ret >= 0)
424                         ret = put_free_dqblk(info, buf, blk);
425                 if (ret < 0) {
426                         quota_error(dquot->dq_sb, "Can't move quota data block "
427                                     "(%u) to free list", blk);
428                         goto out_buf;
429                 }
430         } else {
431                 memset(buf +
432                        (dquot->dq_off & ((1 << info->dqi_blocksize_bits) - 1)),
433                        0, info->dqi_entry_size);
434                 if (le16_to_cpu(dh->dqdh_entries) ==
435                     qtree_dqstr_in_blk(info) - 1) {
436                         /* Insert will write block itself */
437                         ret = insert_free_dqentry(info, buf, blk);
438                         if (ret < 0) {
439                                 quota_error(dquot->dq_sb, "Can't insert quota "
440                                     "data block (%u) to free entry list", blk);
441                                 goto out_buf;
442                         }
443                 } else {
444                         ret = write_blk(info, blk, buf);
445                         if (ret < 0) {
446                                 quota_error(dquot->dq_sb, "Can't write quota "
447                                             "data block %u", blk);
448                                 goto out_buf;
449                         }
450                 }
451         }
452         dquot->dq_off = 0;      /* Quota is now unattached */
453 out_buf:
454         kfree(buf);
455         return ret;
456 }
457
458 /* Remove reference to dquot from tree */
459 static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
460                        uint *blk, int depth)
461 {
462         char *buf = getdqbuf(info->dqi_usable_bs);
463         int ret = 0;
464         uint newblk;
465         __le32 *ref = (__le32 *)buf;
466
467         if (!buf)
468                 return -ENOMEM;
469         ret = read_blk(info, *blk, buf);
470         if (ret < 0) {
471                 quota_error(dquot->dq_sb, "Can't read quota data "
472                             "block %u", blk);
473                 goto out_buf;
474         }
475         newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
476         if (depth == info->dqi_qtree_depth - 1) {
477                 ret = free_dqentry(info, dquot, newblk);
478                 newblk = 0;
479         } else {
480                 ret = remove_tree(info, dquot, &newblk, depth+1);
481         }
482         if (ret >= 0 && !newblk) {
483                 int i;
484                 ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0);
485                 /* Block got empty? */
486                 for (i = 0; i < (info->dqi_usable_bs >> 2) && !ref[i]; i++)
487                         ;
488                 /* Don't put the root block into the free block list */
489                 if (i == (info->dqi_usable_bs >> 2)
490                     && *blk != QT_TREEOFF) {
491                         put_free_dqblk(info, buf, *blk);
492                         *blk = 0;
493                 } else {
494                         ret = write_blk(info, *blk, buf);
495                         if (ret < 0)
496                                 quota_error(dquot->dq_sb, "Can't write quota "
497                                             "tree block %u", blk);
498                 }
499         }
500 out_buf:
501         kfree(buf);
502         return ret;
503 }
504
505 /* Delete dquot from tree */
506 int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
507 {
508         uint tmp = QT_TREEOFF;
509
510         if (!dquot->dq_off)     /* Even not allocated? */
511                 return 0;
512         return remove_tree(info, dquot, &tmp, 0);
513 }
514 EXPORT_SYMBOL(qtree_delete_dquot);
515
516 /* Find entry in block */
517 static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
518                                  struct dquot *dquot, uint blk)
519 {
520         char *buf = getdqbuf(info->dqi_usable_bs);
521         loff_t ret = 0;
522         int i;
523         char *ddquot;
524
525         if (!buf)
526                 return -ENOMEM;
527         ret = read_blk(info, blk, buf);
528         if (ret < 0) {
529                 quota_error(dquot->dq_sb, "Can't read quota tree "
530                             "block %u", blk);
531                 goto out_buf;
532         }
533         ddquot = buf + sizeof(struct qt_disk_dqdbheader);
534         for (i = 0; i < qtree_dqstr_in_blk(info); i++) {
535                 if (info->dqi_ops->is_id(ddquot, dquot))
536                         break;
537                 ddquot += info->dqi_entry_size;
538         }
539         if (i == qtree_dqstr_in_blk(info)) {
540                 quota_error(dquot->dq_sb, "Quota for id %u referenced "
541                             "but not present", dquot->dq_id);
542                 ret = -EIO;
543                 goto out_buf;
544         } else {
545                 ret = (blk << info->dqi_blocksize_bits) + sizeof(struct
546                   qt_disk_dqdbheader) + i * info->dqi_entry_size;
547         }
548 out_buf:
549         kfree(buf);
550         return ret;
551 }
552
553 /* Find entry for given id in the tree */
554 static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
555                                 struct dquot *dquot, uint blk, int depth)
556 {
557         char *buf = getdqbuf(info->dqi_usable_bs);
558         loff_t ret = 0;
559         __le32 *ref = (__le32 *)buf;
560
561         if (!buf)
562                 return -ENOMEM;
563         ret = read_blk(info, blk, buf);
564         if (ret < 0) {
565                 quota_error(dquot->dq_sb, "Can't read quota tree block %u",
566                             blk);
567                 goto out_buf;
568         }
569         ret = 0;
570         blk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
571         if (!blk)       /* No reference? */
572                 goto out_buf;
573         if (depth < info->dqi_qtree_depth - 1)
574                 ret = find_tree_dqentry(info, dquot, blk, depth+1);
575         else
576                 ret = find_block_dqentry(info, dquot, blk);
577 out_buf:
578         kfree(buf);
579         return ret;
580 }
581
582 /* Find entry for given id in the tree - wrapper function */
583 static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info,
584                                   struct dquot *dquot)
585 {
586         return find_tree_dqentry(info, dquot, QT_TREEOFF, 0);
587 }
588
589 int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
590 {
591         int type = dquot->dq_type;
592         struct super_block *sb = dquot->dq_sb;
593         loff_t offset;
594         char *ddquot;
595         int ret = 0;
596
597 #ifdef __QUOTA_QT_PARANOIA
598         /* Invalidated quota? */
599         if (!sb_dqopt(dquot->dq_sb)->files[type]) {
600                 quota_error(sb, "Quota invalidated while reading!");
601                 return -EIO;
602         }
603 #endif
604         /* Do we know offset of the dquot entry in the quota file? */
605         if (!dquot->dq_off) {
606                 offset = find_dqentry(info, dquot);
607                 if (offset <= 0) {      /* Entry not present? */
608                         if (offset < 0)
609                                 quota_error(sb, "Can't read quota structure "
610                                             "for id %u", dquot->dq_id);
611                         dquot->dq_off = 0;
612                         set_bit(DQ_FAKE_B, &dquot->dq_flags);
613                         memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
614                         ret = offset;
615                         goto out;
616                 }
617                 dquot->dq_off = offset;
618         }
619         ddquot = getdqbuf(info->dqi_entry_size);
620         if (!ddquot)
621                 return -ENOMEM;
622         ret = sb->s_op->quota_read(sb, type, ddquot, info->dqi_entry_size,
623                                    dquot->dq_off);
624         if (ret != info->dqi_entry_size) {
625                 if (ret >= 0)
626                         ret = -EIO;
627                 quota_error(sb, "Error while reading quota structure for id %u",
628                             dquot->dq_id);
629                 set_bit(DQ_FAKE_B, &dquot->dq_flags);
630                 memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
631                 kfree(ddquot);
632                 goto out;
633         }
634         spin_lock(&dq_data_lock);
635         info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
636         if (!dquot->dq_dqb.dqb_bhardlimit &&
637             !dquot->dq_dqb.dqb_bsoftlimit &&
638             !dquot->dq_dqb.dqb_ihardlimit &&
639             !dquot->dq_dqb.dqb_isoftlimit)
640                 set_bit(DQ_FAKE_B, &dquot->dq_flags);
641         spin_unlock(&dq_data_lock);
642         kfree(ddquot);
643 out:
644         dqstats_inc(DQST_READS);
645         return ret;
646 }
647 EXPORT_SYMBOL(qtree_read_dquot);
648
649 /* Check whether dquot should not be deleted. We know we are
650  * the only one operating on dquot (thanks to dq_lock) */
651 int qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
652 {
653         if (test_bit(DQ_FAKE_B, &dquot->dq_flags) &&
654             !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace))
655                 return qtree_delete_dquot(info, dquot);
656         return 0;
657 }
658 EXPORT_SYMBOL(qtree_release_dquot);