Commit | Line | Data |
---|---|---|
1ccd14b9 JK |
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 | ||
1ccd14b9 JK |
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 */ | |
7a2435d8 | 36 | static int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info) |
1ccd14b9 JK |
37 | { |
38 | return (info->dqi_usable_bs - sizeof(struct qt_disk_dqdbheader)) | |
39 | / info->dqi_entry_size; | |
40 | } | |
41 | ||
d26ac1a8 | 42 | static char *getdqbuf(size_t size) |
1ccd14b9 | 43 | { |
d26ac1a8 | 44 | char *buf = kmalloc(size, GFP_NOFS); |
1ccd14b9 | 45 | if (!buf) |
268157ba JK |
46 | printk(KERN_WARNING |
47 | "VFS: Not enough memory for quota buffers.\n"); | |
1ccd14b9 JK |
48 | return buf; |
49 | } | |
50 | ||
7a2435d8 | 51 | static ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) |
1ccd14b9 JK |
52 | { |
53 | struct super_block *sb = info->dqi_sb; | |
54 | ||
55 | memset(buf, 0, info->dqi_usable_bs); | |
d26ac1a8 | 56 | return sb->s_op->quota_read(sb, info->dqi_type, buf, |
1ccd14b9 JK |
57 | info->dqi_usable_bs, blk << info->dqi_blocksize_bits); |
58 | } | |
59 | ||
7a2435d8 | 60 | static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) |
1ccd14b9 JK |
61 | { |
62 | struct super_block *sb = info->dqi_sb; | |
1907131b | 63 | ssize_t ret; |
1ccd14b9 | 64 | |
1907131b | 65 | ret = sb->s_op->quota_write(sb, info->dqi_type, buf, |
1ccd14b9 | 66 | info->dqi_usable_bs, blk << info->dqi_blocksize_bits); |
1907131b | 67 | if (ret != info->dqi_usable_bs) { |
fb5ffb0e | 68 | quota_error(sb, "dquota write failed"); |
1907131b JZ |
69 | if (ret >= 0) |
70 | ret = -EIO; | |
71 | } | |
72 | return ret; | |
1ccd14b9 JK |
73 | } |
74 | ||
75 | /* Remove empty block from list and return it */ | |
76 | static int get_free_dqblk(struct qtree_mem_dqinfo *info) | |
77 | { | |
d26ac1a8 | 78 | char *buf = getdqbuf(info->dqi_usable_bs); |
1ccd14b9 JK |
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: | |
d26ac1a8 | 102 | kfree(buf); |
1ccd14b9 JK |
103 | return ret; |
104 | } | |
105 | ||
106 | /* Insert empty block to the list */ | |
d26ac1a8 | 107 | static int put_free_dqblk(struct qtree_mem_dqinfo *info, char *buf, uint blk) |
1ccd14b9 JK |
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 */ | |
268157ba JK |
124 | static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf, |
125 | uint blk) | |
1ccd14b9 | 126 | { |
d26ac1a8 | 127 | char *tmpbuf = getdqbuf(info->dqi_usable_bs); |
1ccd14b9 JK |
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 | } | |
d26ac1a8 | 158 | kfree(tmpbuf); |
1ccd14b9 JK |
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) | |
fb5ffb0e JZ |
162 | quota_error(info->dqi_sb, "Can't write block (%u) " |
163 | "with free entries", blk); | |
1ccd14b9 JK |
164 | return 0; |
165 | out_buf: | |
d26ac1a8 | 166 | kfree(tmpbuf); |
1ccd14b9 JK |
167 | return err; |
168 | } | |
169 | ||
170 | /* Insert given block to the beginning of list with free entries */ | |
268157ba JK |
171 | static int insert_free_dqentry(struct qtree_mem_dqinfo *info, char *buf, |
172 | uint blk) | |
1ccd14b9 | 173 | { |
d26ac1a8 | 174 | char *tmpbuf = getdqbuf(info->dqi_usable_bs); |
1ccd14b9 JK |
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 | } | |
d26ac1a8 | 195 | kfree(tmpbuf); |
1ccd14b9 JK |
196 | info->dqi_free_entry = blk; |
197 | mark_info_dirty(info->dqi_sb, info->dqi_type); | |
198 | return 0; | |
199 | out_buf: | |
d26ac1a8 | 200 | kfree(tmpbuf); |
1ccd14b9 JK |
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; | |
d26ac1a8 | 222 | char *buf = getdqbuf(info->dqi_usable_bs); |
1ccd14b9 JK |
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; | |
d26ac1a8 | 240 | kfree(buf); |
1ccd14b9 JK |
241 | return 0; |
242 | } | |
243 | memset(buf, 0, info->dqi_usable_bs); | |
268157ba JK |
244 | /* This is enough as the block is already zeroed and the entry |
245 | * list is empty... */ | |
1ccd14b9 JK |
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) { | |
fb5ffb0e JZ |
253 | quota_error(dquot->dq_sb, "Can't remove block (%u) " |
254 | "from entry free list", blk); | |
1ccd14b9 JK |
255 | goto out_buf; |
256 | } | |
257 | } | |
258 | le16_add_cpu(&dh->dqdh_entries, 1); | |
259 | /* Find free structure in block */ | |
268157ba JK |
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 | } | |
1ccd14b9 JK |
266 | #ifdef __QUOTA_QT_PARANOIA |
267 | if (i == qtree_dqstr_in_blk(info)) { | |
fb5ffb0e | 268 | quota_error(dquot->dq_sb, "Data block full but it shouldn't"); |
1ccd14b9 JK |
269 | *err = -EIO; |
270 | goto out_buf; | |
271 | } | |
272 | #endif | |
273 | *err = write_blk(info, blk, buf); | |
274 | if (*err < 0) { | |
fb5ffb0e JZ |
275 | quota_error(dquot->dq_sb, "Can't write quota data block %u", |
276 | blk); | |
1ccd14b9 JK |
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; | |
d26ac1a8 | 282 | kfree(buf); |
1ccd14b9 JK |
283 | return blk; |
284 | out_buf: | |
d26ac1a8 | 285 | kfree(buf); |
1ccd14b9 JK |
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 | { | |
d26ac1a8 | 293 | char *buf = getdqbuf(info->dqi_usable_bs); |
1ccd14b9 JK |
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) { | |
fb5ffb0e JZ |
310 | quota_error(dquot->dq_sb, "Can't read tree quota " |
311 | "block %u", *treeblk); | |
1ccd14b9 JK |
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) { | |
fb5ffb0e JZ |
322 | quota_error(dquot->dq_sb, "Inserting already present " |
323 | "quota entry (block %u)", | |
324 | le32_to_cpu(ref[get_index(info, | |
1ccd14b9 JK |
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: | |
d26ac1a8 | 342 | kfree(buf); |
1ccd14b9 JK |
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 | /* | |
268157ba JK |
355 | * We don't have to be afraid of deadlocks as we never have quotas on quota |
356 | * files... | |
1ccd14b9 JK |
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; | |
d26ac1a8 | 363 | char *ddquot = getdqbuf(info->dqi_entry_size); |
1ccd14b9 JK |
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) { | |
fb5ffb0e JZ |
372 | quota_error(sb, "Error %zd occurred while creating " |
373 | "quota", ret); | |
d26ac1a8 | 374 | kfree(ddquot); |
1ccd14b9 JK |
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); | |
d26ac1a8 JK |
381 | ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size, |
382 | dquot->dq_off); | |
1ccd14b9 | 383 | if (ret != info->dqi_entry_size) { |
fb5ffb0e | 384 | quota_error(sb, "dquota write failed"); |
1ccd14b9 JK |
385 | if (ret >= 0) |
386 | ret = -ENOSPC; | |
387 | } else { | |
388 | ret = 0; | |
389 | } | |
dde95888 | 390 | dqstats_inc(DQST_WRITES); |
d26ac1a8 | 391 | kfree(ddquot); |
1ccd14b9 JK |
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; | |
d26ac1a8 | 402 | char *buf = getdqbuf(info->dqi_usable_bs); |
1ccd14b9 JK |
403 | int ret = 0; |
404 | ||
405 | if (!buf) | |
406 | return -ENOMEM; | |
407 | if (dquot->dq_off >> info->dqi_blocksize_bits != blk) { | |
fb5ffb0e JZ |
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)); | |
1ccd14b9 JK |
411 | goto out_buf; |
412 | } | |
413 | ret = read_blk(info, blk, buf); | |
414 | if (ret < 0) { | |
fb5ffb0e JZ |
415 | quota_error(dquot->dq_sb, "Can't read quota data block %u", |
416 | blk); | |
1ccd14b9 JK |
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) { | |
fb5ffb0e JZ |
426 | quota_error(dquot->dq_sb, "Can't move quota data block " |
427 | "(%u) to free list", blk); | |
1ccd14b9 JK |
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) { | |
fb5ffb0e JZ |
439 | quota_error(dquot->dq_sb, "Can't insert quota " |
440 | "data block (%u) to free entry list", blk); | |
1ccd14b9 JK |
441 | goto out_buf; |
442 | } | |
443 | } else { | |
444 | ret = write_blk(info, blk, buf); | |
445 | if (ret < 0) { | |
fb5ffb0e JZ |
446 | quota_error(dquot->dq_sb, "Can't write quota " |
447 | "data block %u", blk); | |
1ccd14b9 JK |
448 | goto out_buf; |
449 | } | |
450 | } | |
451 | } | |
452 | dquot->dq_off = 0; /* Quota is now unattached */ | |
453 | out_buf: | |
d26ac1a8 | 454 | kfree(buf); |
1ccd14b9 JK |
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 | { | |
d26ac1a8 | 462 | char *buf = getdqbuf(info->dqi_usable_bs); |
1ccd14b9 JK |
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) { | |
055adcbd JP |
471 | quota_error(dquot->dq_sb, "Can't read quota data block %u", |
472 | *blk); | |
1ccd14b9 JK |
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? */ | |
d26ac1a8 JK |
486 | for (i = 0; i < (info->dqi_usable_bs >> 2) && !ref[i]; i++) |
487 | ; | |
1ccd14b9 JK |
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) | |
055adcbd JP |
496 | quota_error(dquot->dq_sb, |
497 | "Can't write quota tree block %u", | |
498 | *blk); | |
1ccd14b9 JK |
499 | } |
500 | } | |
501 | out_buf: | |
d26ac1a8 | 502 | kfree(buf); |
1ccd14b9 JK |
503 | return ret; |
504 | } | |
505 | ||
506 | /* Delete dquot from tree */ | |
507 | int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) | |
508 | { | |
509 | uint tmp = QT_TREEOFF; | |
510 | ||
511 | if (!dquot->dq_off) /* Even not allocated? */ | |
512 | return 0; | |
513 | return remove_tree(info, dquot, &tmp, 0); | |
514 | } | |
515 | EXPORT_SYMBOL(qtree_delete_dquot); | |
516 | ||
517 | /* Find entry in block */ | |
518 | static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info, | |
519 | struct dquot *dquot, uint blk) | |
520 | { | |
d26ac1a8 | 521 | char *buf = getdqbuf(info->dqi_usable_bs); |
1ccd14b9 JK |
522 | loff_t ret = 0; |
523 | int i; | |
524 | char *ddquot; | |
525 | ||
526 | if (!buf) | |
527 | return -ENOMEM; | |
528 | ret = read_blk(info, blk, buf); | |
529 | if (ret < 0) { | |
fb5ffb0e JZ |
530 | quota_error(dquot->dq_sb, "Can't read quota tree " |
531 | "block %u", blk); | |
1ccd14b9 JK |
532 | goto out_buf; |
533 | } | |
268157ba JK |
534 | ddquot = buf + sizeof(struct qt_disk_dqdbheader); |
535 | for (i = 0; i < qtree_dqstr_in_blk(info); i++) { | |
536 | if (info->dqi_ops->is_id(ddquot, dquot)) | |
537 | break; | |
538 | ddquot += info->dqi_entry_size; | |
539 | } | |
1ccd14b9 | 540 | if (i == qtree_dqstr_in_blk(info)) { |
fb5ffb0e JZ |
541 | quota_error(dquot->dq_sb, "Quota for id %u referenced " |
542 | "but not present", dquot->dq_id); | |
1ccd14b9 JK |
543 | ret = -EIO; |
544 | goto out_buf; | |
545 | } else { | |
546 | ret = (blk << info->dqi_blocksize_bits) + sizeof(struct | |
547 | qt_disk_dqdbheader) + i * info->dqi_entry_size; | |
548 | } | |
549 | out_buf: | |
d26ac1a8 | 550 | kfree(buf); |
1ccd14b9 JK |
551 | return ret; |
552 | } | |
553 | ||
554 | /* Find entry for given id in the tree */ | |
555 | static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info, | |
556 | struct dquot *dquot, uint blk, int depth) | |
557 | { | |
d26ac1a8 | 558 | char *buf = getdqbuf(info->dqi_usable_bs); |
1ccd14b9 JK |
559 | loff_t ret = 0; |
560 | __le32 *ref = (__le32 *)buf; | |
561 | ||
562 | if (!buf) | |
563 | return -ENOMEM; | |
564 | ret = read_blk(info, blk, buf); | |
565 | if (ret < 0) { | |
fb5ffb0e JZ |
566 | quota_error(dquot->dq_sb, "Can't read quota tree block %u", |
567 | blk); | |
1ccd14b9 JK |
568 | goto out_buf; |
569 | } | |
570 | ret = 0; | |
571 | blk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); | |
572 | if (!blk) /* No reference? */ | |
573 | goto out_buf; | |
574 | if (depth < info->dqi_qtree_depth - 1) | |
575 | ret = find_tree_dqentry(info, dquot, blk, depth+1); | |
576 | else | |
577 | ret = find_block_dqentry(info, dquot, blk); | |
578 | out_buf: | |
d26ac1a8 | 579 | kfree(buf); |
1ccd14b9 JK |
580 | return ret; |
581 | } | |
582 | ||
583 | /* Find entry for given id in the tree - wrapper function */ | |
584 | static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info, | |
585 | struct dquot *dquot) | |
586 | { | |
587 | return find_tree_dqentry(info, dquot, QT_TREEOFF, 0); | |
588 | } | |
589 | ||
590 | int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) | |
591 | { | |
592 | int type = dquot->dq_type; | |
593 | struct super_block *sb = dquot->dq_sb; | |
594 | loff_t offset; | |
d26ac1a8 | 595 | char *ddquot; |
1ccd14b9 JK |
596 | int ret = 0; |
597 | ||
598 | #ifdef __QUOTA_QT_PARANOIA | |
599 | /* Invalidated quota? */ | |
600 | if (!sb_dqopt(dquot->dq_sb)->files[type]) { | |
fb5ffb0e | 601 | quota_error(sb, "Quota invalidated while reading!"); |
1ccd14b9 JK |
602 | return -EIO; |
603 | } | |
604 | #endif | |
605 | /* Do we know offset of the dquot entry in the quota file? */ | |
606 | if (!dquot->dq_off) { | |
607 | offset = find_dqentry(info, dquot); | |
608 | if (offset <= 0) { /* Entry not present? */ | |
609 | if (offset < 0) | |
fb5ffb0e JZ |
610 | quota_error(sb, "Can't read quota structure " |
611 | "for id %u", dquot->dq_id); | |
1ccd14b9 JK |
612 | dquot->dq_off = 0; |
613 | set_bit(DQ_FAKE_B, &dquot->dq_flags); | |
614 | memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); | |
615 | ret = offset; | |
616 | goto out; | |
617 | } | |
618 | dquot->dq_off = offset; | |
619 | } | |
620 | ddquot = getdqbuf(info->dqi_entry_size); | |
621 | if (!ddquot) | |
622 | return -ENOMEM; | |
d26ac1a8 JK |
623 | ret = sb->s_op->quota_read(sb, type, ddquot, info->dqi_entry_size, |
624 | dquot->dq_off); | |
1ccd14b9 JK |
625 | if (ret != info->dqi_entry_size) { |
626 | if (ret >= 0) | |
627 | ret = -EIO; | |
fb5ffb0e JZ |
628 | quota_error(sb, "Error while reading quota structure for id %u", |
629 | dquot->dq_id); | |
1ccd14b9 JK |
630 | set_bit(DQ_FAKE_B, &dquot->dq_flags); |
631 | memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); | |
d26ac1a8 | 632 | kfree(ddquot); |
1ccd14b9 JK |
633 | goto out; |
634 | } | |
635 | spin_lock(&dq_data_lock); | |
636 | info->dqi_ops->disk2mem_dqblk(dquot, ddquot); | |
637 | if (!dquot->dq_dqb.dqb_bhardlimit && | |
638 | !dquot->dq_dqb.dqb_bsoftlimit && | |
639 | !dquot->dq_dqb.dqb_ihardlimit && | |
640 | !dquot->dq_dqb.dqb_isoftlimit) | |
641 | set_bit(DQ_FAKE_B, &dquot->dq_flags); | |
642 | spin_unlock(&dq_data_lock); | |
d26ac1a8 | 643 | kfree(ddquot); |
1ccd14b9 | 644 | out: |
dde95888 | 645 | dqstats_inc(DQST_READS); |
1ccd14b9 JK |
646 | return ret; |
647 | } | |
648 | EXPORT_SYMBOL(qtree_read_dquot); | |
649 | ||
650 | /* Check whether dquot should not be deleted. We know we are | |
651 | * the only one operating on dquot (thanks to dq_lock) */ | |
652 | int qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) | |
653 | { | |
268157ba JK |
654 | if (test_bit(DQ_FAKE_B, &dquot->dq_flags) && |
655 | !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) | |
1ccd14b9 JK |
656 | return qtree_delete_dquot(info, dquot); |
657 | return 0; | |
658 | } | |
659 | EXPORT_SYMBOL(qtree_release_dquot); |