blob: c1653a391a3a74520597bd0c8fe18baec973ddf0 [file] [log] [blame]
Aditya Kalif239fef2011-07-20 11:40:02 -07001/*
2 * Implementation of new quotafile format
3 *
4 * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
5 */
6
Theodore Ts'od1154eb2011-09-18 17:34:37 -04007#include "config.h"
Aditya Kalif239fef2011-07-20 11:40:02 -07008#include <sys/types.h>
9#include <errno.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
Aditya Kalif239fef2011-07-20 11:40:02 -070014
15#include "common.h"
16#include "quotaio_tree.h"
17#include "quotaio.h"
18
19typedef char *dqbuf_t;
20
Aditya Kalia86d55d2011-11-14 10:55:54 -050021#define freedqbuf(buf) ext2fs_free_mem(&buf)
22
23static inline dqbuf_t getdqbuf(void)
24{
25 dqbuf_t buf;
26 if (ext2fs_get_memzero(QT_BLKSIZE, &buf)) {
Andreas Dilger0ce01722012-11-29 05:47:54 -070027 log_err("Failed to allocate dqbuf");
Aditya Kalia86d55d2011-11-14 10:55:54 -050028 return NULL;
29 }
30
31 return buf;
32}
Aditya Kalif239fef2011-07-20 11:40:02 -070033
34/* Is given dquot empty? */
35int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
36{
37 int i;
38
39 for (i = 0; i < info->dqi_entry_size; i++)
40 if (disk[i])
41 return 0;
42 return 1;
43}
44
45int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
46{
47 return (QT_BLKSIZE - sizeof(struct qt_disk_dqdbheader)) /
48 info->dqi_entry_size;
49}
50
51static int get_index(qid_t id, int depth)
52{
53 return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff;
54}
55
Theodore Ts'o577c7732013-05-19 20:03:48 -040056static inline void mark_quotafile_info_dirty(struct quota_handle *h)
57{
58 h->qh_io_flags |= IOFL_INFODIRTY;
59}
60
Aditya Kalif239fef2011-07-20 11:40:02 -070061/* Read given block */
62static void read_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
63{
64 int err;
65
66 err = h->e2fs_read(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
67 QT_BLKSIZE);
68 if (err < 0)
Aditya Kalia86d55d2011-11-14 10:55:54 -050069 log_err("Cannot read block %u: %s", blk, strerror(errno));
Aditya Kalif239fef2011-07-20 11:40:02 -070070 else if (err != QT_BLKSIZE)
71 memset(buf + err, 0, QT_BLKSIZE - err);
72}
73
74/* Write block */
75static int write_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
76{
77 int err;
78
79 err = h->e2fs_write(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
80 QT_BLKSIZE);
81 if (err < 0 && errno != ENOSPC)
Aditya Kalia86d55d2011-11-14 10:55:54 -050082 log_err("Cannot write block (%u): %s", blk, strerror(errno));
Aditya Kalif239fef2011-07-20 11:40:02 -070083 if (err != QT_BLKSIZE)
84 return -ENOSPC;
85 return 0;
86}
87
88/* Get free block in file (either from free list or create new one) */
89static int get_free_dqblk(struct quota_handle *h)
90{
91 dqbuf_t buf = getdqbuf();
92 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
93 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
94 int blk;
95
Aditya Kalia86d55d2011-11-14 10:55:54 -050096 if (!buf)
97 return -ENOMEM;
98
Aditya Kalif239fef2011-07-20 11:40:02 -070099 if (info->dqi_free_blk) {
100 blk = info->dqi_free_blk;
101 read_blk(h, blk, buf);
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400102 info->dqi_free_blk = ext2fs_le32_to_cpu(dh->dqdh_next_free);
Aditya Kalif239fef2011-07-20 11:40:02 -0700103 } else {
104 memset(buf, 0, QT_BLKSIZE);
105 /* Assure block allocation... */
106 if (write_blk(h, info->dqi_blocks, buf) < 0) {
107 freedqbuf(buf);
108 log_err("Cannot allocate new quota block "
Andreas Dilger0ce01722012-11-29 05:47:54 -0700109 "(out of disk space).");
Aditya Kalif239fef2011-07-20 11:40:02 -0700110 return -ENOSPC;
111 }
112 blk = info->dqi_blocks++;
113 }
114 mark_quotafile_info_dirty(h);
115 freedqbuf(buf);
116 return blk;
117}
118
119/* Put given block to free list */
120static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf, uint blk)
121{
122 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
123 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
124
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400125 dh->dqdh_next_free = ext2fs_cpu_to_le32(info->dqi_free_blk);
126 dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
127 dh->dqdh_entries = ext2fs_cpu_to_le16(0);
Aditya Kalif239fef2011-07-20 11:40:02 -0700128 info->dqi_free_blk = blk;
129 mark_quotafile_info_dirty(h);
130 write_blk(h, blk, buf);
131}
132
133/* Remove given block from the list of blocks with free entries */
134static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
135{
136 dqbuf_t tmpbuf = getdqbuf();
137 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400138 uint nextblk = ext2fs_le32_to_cpu(dh->dqdh_next_free), prevblk =
Aditya Kalif239fef2011-07-20 11:40:02 -0700139
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400140 ext2fs_le32_to_cpu(dh->dqdh_prev_free);
Aditya Kalif239fef2011-07-20 11:40:02 -0700141
Aditya Kalia86d55d2011-11-14 10:55:54 -0500142 if (!tmpbuf)
143 return;
144
Aditya Kalif239fef2011-07-20 11:40:02 -0700145 if (nextblk) {
146 read_blk(h, nextblk, tmpbuf);
147 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
148 dh->dqdh_prev_free;
149 write_blk(h, nextblk, tmpbuf);
150 }
151 if (prevblk) {
152 read_blk(h, prevblk, tmpbuf);
153 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
154 dh->dqdh_next_free;
155 write_blk(h, prevblk, tmpbuf);
156 } else {
157 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk;
158 mark_quotafile_info_dirty(h);
159 }
160 freedqbuf(tmpbuf);
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400161 dh->dqdh_next_free = dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
Aditya Kalif239fef2011-07-20 11:40:02 -0700162 write_blk(h, blk, buf); /* No matter whether write succeeds
163 * block is out of list */
164}
165
166/* Insert given block to the beginning of list with free entries */
167static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
168{
169 dqbuf_t tmpbuf = getdqbuf();
170 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
171 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
172
Aditya Kalia86d55d2011-11-14 10:55:54 -0500173 if (!tmpbuf)
174 return;
175
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400176 dh->dqdh_next_free = ext2fs_cpu_to_le32(info->dqi_free_entry);
177 dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
Aditya Kalif239fef2011-07-20 11:40:02 -0700178 write_blk(h, blk, buf);
179 if (info->dqi_free_entry) {
180 read_blk(h, info->dqi_free_entry, tmpbuf);
181 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400182 ext2fs_cpu_to_le32(blk);
Aditya Kalif239fef2011-07-20 11:40:02 -0700183 write_blk(h, info->dqi_free_entry, tmpbuf);
184 }
185 freedqbuf(tmpbuf);
186 info->dqi_free_entry = blk;
187 mark_quotafile_info_dirty(h);
188}
189
190/* Find space for dquot */
191static uint find_free_dqentry(struct quota_handle *h, struct dquot *dquot,
192 int *err)
193{
194 int blk, i;
195 struct qt_disk_dqdbheader *dh;
196 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
197 char *ddquot;
198 dqbuf_t buf;
199
200 *err = 0;
201 buf = getdqbuf();
Aditya Kalia86d55d2011-11-14 10:55:54 -0500202 if (!buf) {
203 *err = -ENOMEM;
204 return 0;
205 }
206
Aditya Kalif239fef2011-07-20 11:40:02 -0700207 dh = (struct qt_disk_dqdbheader *)buf;
208 if (info->dqi_free_entry) {
209 blk = info->dqi_free_entry;
210 read_blk(h, blk, buf);
211 } else {
212 blk = get_free_dqblk(h);
213 if (blk < 0) {
214 freedqbuf(buf);
215 *err = blk;
216 return 0;
217 }
218 memset(buf, 0, QT_BLKSIZE);
219 info->dqi_free_entry = blk;
220 mark_quotafile_info_dirty(h);
221 }
222
223 /* Block will be full? */
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400224 if (ext2fs_le16_to_cpu(dh->dqdh_entries) + 1 >=
225 qtree_dqstr_in_blk(info))
Aditya Kalif239fef2011-07-20 11:40:02 -0700226 remove_free_dqentry(h, buf, blk);
227
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400228 dh->dqdh_entries =
229 ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) + 1);
Aditya Kalif239fef2011-07-20 11:40:02 -0700230 /* Find free structure in block */
231 ddquot = buf + sizeof(struct qt_disk_dqdbheader);
232 for (i = 0;
233 i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot);
234 i++)
235 ddquot += info->dqi_entry_size;
236
237 if (i == qtree_dqstr_in_blk(info))
Andreas Dilger0ce01722012-11-29 05:47:54 -0700238 log_err("find_free_dqentry(): Data block full unexpectedly.");
Aditya Kalif239fef2011-07-20 11:40:02 -0700239
240 write_blk(h, blk, buf);
241 dquot->dq_dqb.u.v2_mdqb.dqb_off =
242 (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
243 i * info->dqi_entry_size;
244 freedqbuf(buf);
245 return blk;
246}
247
248/* Insert reference to structure into the trie */
249static int do_insert_tree(struct quota_handle *h, struct dquot *dquot,
250 uint * treeblk, int depth)
251{
252 dqbuf_t buf;
253 int newson = 0, newact = 0;
254 u_int32_t *ref;
255 uint newblk;
256 int ret = 0;
257
258 log_debug("inserting in tree: treeblk=%u, depth=%d", *treeblk, depth);
259 buf = getdqbuf();
Aditya Kalia86d55d2011-11-14 10:55:54 -0500260 if (!buf)
261 return -ENOMEM;
262
Aditya Kalif239fef2011-07-20 11:40:02 -0700263 if (!*treeblk) {
264 ret = get_free_dqblk(h);
265 if (ret < 0)
266 goto out_buf;
267 *treeblk = ret;
268 memset(buf, 0, QT_BLKSIZE);
269 newact = 1;
270 } else {
271 read_blk(h, *treeblk, buf);
272 }
273
274 ref = (u_int32_t *) buf;
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400275 newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
Aditya Kalif239fef2011-07-20 11:40:02 -0700276 if (!newblk)
277 newson = 1;
278 if (depth == QT_TREEDEPTH - 1) {
279 if (newblk)
Aditya Kalia86d55d2011-11-14 10:55:54 -0500280 log_err("Inserting already present quota entry "
281 "(block %u).",
282 ref[get_index(dquot->dq_id, depth)]);
Aditya Kalif239fef2011-07-20 11:40:02 -0700283 newblk = find_free_dqentry(h, dquot, &ret);
284 } else {
285 ret = do_insert_tree(h, dquot, &newblk, depth + 1);
286 }
287
288 if (newson && ret >= 0) {
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400289 ref[get_index(dquot->dq_id, depth)] =
290 ext2fs_cpu_to_le32(newblk);
Aditya Kalif239fef2011-07-20 11:40:02 -0700291 write_blk(h, *treeblk, buf);
292 } else if (newact && ret < 0) {
293 put_free_dqblk(h, buf, *treeblk);
294 }
295
296out_buf:
297 freedqbuf(buf);
298 return ret;
299}
300
301/* Wrapper for inserting quota structure into tree */
302static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot)
303{
304 uint tmp = QT_TREEOFF;
305
306 if (do_insert_tree(h, dquot, &tmp, 0) < 0)
Aditya Kalia86d55d2011-11-14 10:55:54 -0500307 log_err("Cannot write quota (id %u): %s",
308 (uint) dquot->dq_id, strerror(errno));
Aditya Kalif239fef2011-07-20 11:40:02 -0700309}
310
311/* Write dquot to file */
312void qtree_write_dquot(struct dquot *dquot)
313{
314 ssize_t ret;
Aditya Kalia86d55d2011-11-14 10:55:54 -0500315 char *ddquot;
Aditya Kalif239fef2011-07-20 11:40:02 -0700316 struct quota_handle *h = dquot->dq_h;
317 struct qtree_mem_dqinfo *info =
318 &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
319 log_debug("writing ddquot 1: off=%llu, info->dqi_entry_size=%u",
320 dquot->dq_dqb.u.v2_mdqb.dqb_off,
321 info->dqi_entry_size);
Aditya Kalia86d55d2011-11-14 10:55:54 -0500322 ret = ext2fs_get_mem(info->dqi_entry_size, &ddquot);
323 if (ret) {
324 errno = ENOMEM;
325 log_err("Quota write failed (id %u): %s",
326 (uint)dquot->dq_id, strerror(errno));
327 return;
328 }
Aditya Kalif239fef2011-07-20 11:40:02 -0700329
330 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off)
331 dq_insert_tree(dquot->dq_h, dquot);
332 info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
333 log_debug("writing ddquot 2: off=%llu, info->dqi_entry_size=%u",
334 dquot->dq_dqb.u.v2_mdqb.dqb_off,
335 info->dqi_entry_size);
336 ret = h->e2fs_write(&h->qh_qf, dquot->dq_dqb.u.v2_mdqb.dqb_off, ddquot,
337 info->dqi_entry_size);
338
339 if (ret != info->dqi_entry_size) {
340 if (ret > 0)
341 errno = ENOSPC;
Aditya Kalia86d55d2011-11-14 10:55:54 -0500342 log_err("Quota write failed (id %u): %s",
343 (uint)dquot->dq_id, strerror(errno));
Aditya Kalif239fef2011-07-20 11:40:02 -0700344 }
Aditya Kalia86d55d2011-11-14 10:55:54 -0500345 ext2fs_free_mem(&ddquot);
Aditya Kalif239fef2011-07-20 11:40:02 -0700346}
347
348/* Free dquot entry in data block */
349static void free_dqentry(struct quota_handle *h, struct dquot *dquot, uint blk)
350{
351 struct qt_disk_dqdbheader *dh;
352 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
353 dqbuf_t buf = getdqbuf();
354
Aditya Kalia86d55d2011-11-14 10:55:54 -0500355 if (!buf)
356 return;
357
Aditya Kalif239fef2011-07-20 11:40:02 -0700358 if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk)
Aditya Kalia86d55d2011-11-14 10:55:54 -0500359 log_err("Quota structure has offset to other block (%u) "
360 "than it should (%u).", blk,
Aditya Kalif239fef2011-07-20 11:40:02 -0700361 (uint) (dquot->dq_dqb.u.v2_mdqb.dqb_off >>
362 QT_BLKSIZE_BITS));
363
364 read_blk(h, blk, buf);
365 dh = (struct qt_disk_dqdbheader *)buf;
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400366 dh->dqdh_entries =
367 ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) - 1);
Aditya Kalif239fef2011-07-20 11:40:02 -0700368
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400369 if (!ext2fs_le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */
Aditya Kalif239fef2011-07-20 11:40:02 -0700370 remove_free_dqentry(h, buf, blk);
371 put_free_dqblk(h, buf, blk);
372 } else {
373 memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off &
374 ((1 << QT_BLKSIZE_BITS) - 1)),
375 0, info->dqi_entry_size);
376
377 /* First free entry? */
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400378 if (ext2fs_le16_to_cpu(dh->dqdh_entries) ==
Aditya Kalif239fef2011-07-20 11:40:02 -0700379 qtree_dqstr_in_blk(info) - 1)
380 /* This will also write data block */
381 insert_free_dqentry(h, buf, blk);
382 else
383 write_blk(h, blk, buf);
384 }
385 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
386 freedqbuf(buf);
387}
388
389/* Remove reference to dquot from tree */
390static void remove_tree(struct quota_handle *h, struct dquot *dquot,
391 uint * blk, int depth)
392{
393 dqbuf_t buf = getdqbuf();
394 uint newblk;
395 u_int32_t *ref = (u_int32_t *) buf;
396
Aditya Kalia86d55d2011-11-14 10:55:54 -0500397 if (!buf)
398 return;
399
Aditya Kalif239fef2011-07-20 11:40:02 -0700400 read_blk(h, *blk, buf);
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400401 newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
Aditya Kalif239fef2011-07-20 11:40:02 -0700402 if (depth == QT_TREEDEPTH - 1) {
403 free_dqentry(h, dquot, newblk);
404 newblk = 0;
405 } else {
406 remove_tree(h, dquot, &newblk, depth + 1);
407 }
408
409 if (!newblk) {
410 int i;
411
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400412 ref[get_index(dquot->dq_id, depth)] = ext2fs_cpu_to_le32(0);
Aditya Kalif239fef2011-07-20 11:40:02 -0700413
414 /* Block got empty? */
415 for (i = 0; i < QT_BLKSIZE && !buf[i]; i++);
416
417 /* Don't put the root block into the free block list */
418 if (i == QT_BLKSIZE && *blk != QT_TREEOFF) {
419 put_free_dqblk(h, buf, *blk);
420 *blk = 0;
421 } else {
422 write_blk(h, *blk, buf);
423 }
424 }
425 freedqbuf(buf);
426}
427
428/* Delete dquot from tree */
429void qtree_delete_dquot(struct dquot *dquot)
430{
431 uint tmp = QT_TREEOFF;
432
433 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */
434 return;
435 remove_tree(dquot->dq_h, dquot, &tmp, 0);
436}
437
438/* Find entry in block */
Theodore Ts'od6120a22011-10-04 11:46:21 -0400439static ext2_loff_t find_block_dqentry(struct quota_handle *h,
440 struct dquot *dquot, uint blk)
Aditya Kalif239fef2011-07-20 11:40:02 -0700441{
442 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
443 dqbuf_t buf = getdqbuf();
444 int i;
445 char *ddquot = buf + sizeof(struct qt_disk_dqdbheader);
446
Aditya Kalia86d55d2011-11-14 10:55:54 -0500447 if (!buf)
448 return -ENOMEM;
449
Aditya Kalif239fef2011-07-20 11:40:02 -0700450 read_blk(h, blk, buf);
451 for (i = 0;
452 i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot);
453 i++)
454 ddquot += info->dqi_entry_size;
455
456 if (i == qtree_dqstr_in_blk(info))
Aditya Kalia86d55d2011-11-14 10:55:54 -0500457 log_err("Quota for id %u referenced but not present.",
458 dquot->dq_id);
Aditya Kalif239fef2011-07-20 11:40:02 -0700459 freedqbuf(buf);
460 return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
461 i * info->dqi_entry_size;
462}
463
464/* Find entry for given id in the tree */
Theodore Ts'od6120a22011-10-04 11:46:21 -0400465static ext2_loff_t find_tree_dqentry(struct quota_handle *h,
466 struct dquot *dquot,
467 uint blk, int depth)
Aditya Kalif239fef2011-07-20 11:40:02 -0700468{
469 dqbuf_t buf = getdqbuf();
Theodore Ts'od6120a22011-10-04 11:46:21 -0400470 ext2_loff_t ret = 0;
Aditya Kalif239fef2011-07-20 11:40:02 -0700471 u_int32_t *ref = (u_int32_t *) buf;
472
Aditya Kalia86d55d2011-11-14 10:55:54 -0500473 if (!buf)
474 return -ENOMEM;
475
Aditya Kalif239fef2011-07-20 11:40:02 -0700476 read_blk(h, blk, buf);
477 ret = 0;
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400478 blk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
Aditya Kalif239fef2011-07-20 11:40:02 -0700479 if (!blk) /* No reference? */
480 goto out_buf;
481 if (depth < QT_TREEDEPTH - 1)
482 ret = find_tree_dqentry(h, dquot, blk, depth + 1);
483 else
484 ret = find_block_dqentry(h, dquot, blk);
485out_buf:
486 freedqbuf(buf);
487 return ret;
488}
489
490/* Find entry for given id in the tree - wrapper function */
Theodore Ts'od6120a22011-10-04 11:46:21 -0400491static inline ext2_loff_t find_dqentry(struct quota_handle *h,
492 struct dquot *dquot)
Aditya Kalif239fef2011-07-20 11:40:02 -0700493{
494 return find_tree_dqentry(h, dquot, QT_TREEOFF, 0);
495}
496
497/*
Aditya Kalia86d55d2011-11-14 10:55:54 -0500498 * Read dquot from disk.
Aditya Kalif239fef2011-07-20 11:40:02 -0700499 */
500struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
501{
502 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
Theodore Ts'od6120a22011-10-04 11:46:21 -0400503 ext2_loff_t offset;
Aditya Kalif239fef2011-07-20 11:40:02 -0700504 ssize_t ret;
Aditya Kalia86d55d2011-11-14 10:55:54 -0500505 char *ddquot;
Aditya Kalif239fef2011-07-20 11:40:02 -0700506 struct dquot *dquot = get_empty_dquot();
507
Aditya Kalia86d55d2011-11-14 10:55:54 -0500508 if (!dquot)
509 return NULL;
510 if (ext2fs_get_mem(info->dqi_entry_size, &ddquot)) {
511 ext2fs_free_mem(&dquot);
512 return NULL;
513 }
514
Aditya Kalif239fef2011-07-20 11:40:02 -0700515 dquot->dq_id = id;
516 dquot->dq_h = h;
517 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
518 memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
519
520 offset = find_dqentry(h, dquot);
521 if (offset > 0) {
522 dquot->dq_dqb.u.v2_mdqb.dqb_off = offset;
523 ret = h->e2fs_read(&h->qh_qf, offset, ddquot,
524 info->dqi_entry_size);
525 if (ret != info->dqi_entry_size) {
526 if (ret > 0)
527 errno = EIO;
Aditya Kalia86d55d2011-11-14 10:55:54 -0500528 log_err("Cannot read quota structure for id %u: %s",
Aditya Kalif239fef2011-07-20 11:40:02 -0700529 dquot->dq_id, strerror(errno));
530 }
531 info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
532 }
Aditya Kalia86d55d2011-11-14 10:55:54 -0500533 ext2fs_free_mem(&ddquot);
Aditya Kalif239fef2011-07-20 11:40:02 -0700534 return dquot;
535}
536
537/*
Aditya Kalia86d55d2011-11-14 10:55:54 -0500538 * Scan all dquots in file and call callback on each
Aditya Kalif239fef2011-07-20 11:40:02 -0700539 */
540#define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
541#define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
542
543static int report_block(struct dquot *dquot, uint blk, char *bitmap,
Niu198d20f2011-11-14 10:58:28 -0500544 int (*process_dquot) (struct dquot *, void *),
545 void *data)
Aditya Kalif239fef2011-07-20 11:40:02 -0700546{
547 struct qtree_mem_dqinfo *info =
548 &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
549 dqbuf_t buf = getdqbuf();
550 struct qt_disk_dqdbheader *dh;
551 char *ddata;
552 int entries, i;
553
Aditya Kalia86d55d2011-11-14 10:55:54 -0500554 if (!buf)
555 return 0;
556
Aditya Kalif239fef2011-07-20 11:40:02 -0700557 set_bit(bitmap, blk);
558 read_blk(dquot->dq_h, blk, buf);
559 dh = (struct qt_disk_dqdbheader *)buf;
560 ddata = buf + sizeof(struct qt_disk_dqdbheader);
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400561 entries = ext2fs_le16_to_cpu(dh->dqdh_entries);
Aditya Kalif239fef2011-07-20 11:40:02 -0700562 for (i = 0; i < qtree_dqstr_in_blk(info);
563 i++, ddata += info->dqi_entry_size)
564 if (!qtree_entry_unused(info, ddata)) {
Niu198d20f2011-11-14 10:58:28 -0500565 dquot->dq_dqb.u.v2_mdqb.dqb_off =
566 (blk << QT_BLKSIZE_BITS) +
567 sizeof(struct qt_disk_dqdbheader) +
568 i * info->dqi_entry_size;
Aditya Kalif239fef2011-07-20 11:40:02 -0700569 info->dqi_ops->disk2mem_dqblk(dquot, ddata);
Niu198d20f2011-11-14 10:58:28 -0500570 if (process_dquot(dquot, data) < 0)
Aditya Kalif239fef2011-07-20 11:40:02 -0700571 break;
572 }
573 freedqbuf(buf);
574 return entries;
575}
576
577static void check_reference(struct quota_handle *h, uint blk)
578{
579 if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks)
Aditya Kalia86d55d2011-11-14 10:55:54 -0500580 log_err("Illegal reference (%u >= %u) in %s quota file. "
581 "Quota file is probably corrupted.\n"
582 "Please run e2fsck (8) to fix it.",
583 blk,
584 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
585 type2name(h->qh_type));
Aditya Kalif239fef2011-07-20 11:40:02 -0700586}
587
588static int report_tree(struct dquot *dquot, uint blk, int depth, char *bitmap,
Niu198d20f2011-11-14 10:58:28 -0500589 int (*process_dquot) (struct dquot *, void *),
590 void *data)
Aditya Kalif239fef2011-07-20 11:40:02 -0700591{
592 int entries = 0, i;
593 dqbuf_t buf = getdqbuf();
594 u_int32_t *ref = (u_int32_t *) buf;
595
Aditya Kalia86d55d2011-11-14 10:55:54 -0500596 if (!buf)
597 return 0;
598
Aditya Kalif239fef2011-07-20 11:40:02 -0700599 read_blk(dquot->dq_h, blk, buf);
600 if (depth == QT_TREEDEPTH - 1) {
601 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400602 blk = ext2fs_le32_to_cpu(ref[i]);
Aditya Kalif239fef2011-07-20 11:40:02 -0700603 check_reference(dquot->dq_h, blk);
604 if (blk && !get_bit(bitmap, blk))
605 entries += report_block(dquot, blk, bitmap,
Niu198d20f2011-11-14 10:58:28 -0500606 process_dquot, data);
Aditya Kalif239fef2011-07-20 11:40:02 -0700607 }
608 } else {
Niu198d20f2011-11-14 10:58:28 -0500609 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
Theodore Ts'o38a420b2011-09-28 20:08:37 -0400610 blk = ext2fs_le32_to_cpu(ref[i]);
Aditya Kalif239fef2011-07-20 11:40:02 -0700611 if (blk) {
612 check_reference(dquot->dq_h, blk);
613 entries += report_tree(dquot, blk, depth + 1,
Niu198d20f2011-11-14 10:58:28 -0500614 bitmap, process_dquot,
615 data);
Aditya Kalif239fef2011-07-20 11:40:02 -0700616 }
Niu198d20f2011-11-14 10:58:28 -0500617 }
Aditya Kalif239fef2011-07-20 11:40:02 -0700618 }
619 freedqbuf(buf);
620 return entries;
621}
622
623static uint find_set_bits(char *bmp, int blocks)
624{
625 uint i, used = 0;
626
627 for (i = 0; i < blocks; i++)
628 if (get_bit(bmp, i))
629 used++;
630 return used;
631}
632
633int qtree_scan_dquots(struct quota_handle *h,
Niu198d20f2011-11-14 10:58:28 -0500634 int (*process_dquot) (struct dquot *, void *),
635 void *data)
Aditya Kalif239fef2011-07-20 11:40:02 -0700636{
637 char *bitmap;
638 struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
639 struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
640 struct dquot *dquot = get_empty_dquot();
641
Aditya Kalia86d55d2011-11-14 10:55:54 -0500642 if (!dquot)
643 return -1;
644
Aditya Kalif239fef2011-07-20 11:40:02 -0700645 dquot->dq_h = h;
Aditya Kalia86d55d2011-11-14 10:55:54 -0500646 if (ext2fs_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap)) {
647 ext2fs_free_mem(&dquot);
648 return -1;
649 }
Aditya Kalif239fef2011-07-20 11:40:02 -0700650 v2info->dqi_used_entries = report_tree(dquot, QT_TREEOFF, 0, bitmap,
Niu198d20f2011-11-14 10:58:28 -0500651 process_dquot, data);
Aditya Kalif239fef2011-07-20 11:40:02 -0700652 v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
Aditya Kalia86d55d2011-11-14 10:55:54 -0500653 ext2fs_free_mem(&bitmap);
654 ext2fs_free_mem(&dquot);
Aditya Kalif239fef2011-07-20 11:40:02 -0700655 return 0;
656}