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