1 /*
2  * Implementation of new quotafile format
3  *
4  * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
5  */
6 
7 #include <sys/types.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 
14 #include "common.h"
15 #include "quotaio_tree.h"
16 #include "quotaio.h"
17 
18 typedef char *dqbuf_t;
19 
20 #define freedqbuf(buf)		ext2fs_free_mem(&buf)
21 
getdqbuf(void)22 static inline dqbuf_t getdqbuf(void)
23 {
24 	dqbuf_t buf;
25 	if (ext2fs_get_memzero(QT_BLKSIZE, &buf)) {
26 		log_err("Failed to allocate dqbuf");
27 		return NULL;
28 	}
29 
30 	return buf;
31 }
32 
33 /* Is given dquot empty? */
qtree_entry_unused(struct qtree_mem_dqinfo * info,char * disk)34 int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
35 {
36 	int i;
37 
38 	for (i = 0; i < info->dqi_entry_size; i++)
39 		if (disk[i])
40 			return 0;
41 	return 1;
42 }
43 
qtree_dqstr_in_blk(struct qtree_mem_dqinfo * info)44 int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
45 {
46 	return (QT_BLKSIZE - sizeof(struct qt_disk_dqdbheader)) /
47 		info->dqi_entry_size;
48 }
49 
get_index(qid_t id,int depth)50 static int get_index(qid_t id, int depth)
51 {
52 	return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff;
53 }
54 
mark_quotafile_info_dirty(struct quota_handle * h)55 static inline void mark_quotafile_info_dirty(struct quota_handle *h)
56 {
57 	h->qh_io_flags |= IOFL_INFODIRTY;
58 }
59 
60 /* Read given block */
read_blk(struct quota_handle * h,uint blk,dqbuf_t buf)61 static void read_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
62 {
63 	int err;
64 
65 	err = h->e2fs_read(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
66 			QT_BLKSIZE);
67 	if (err < 0)
68 		log_err("Cannot read block %u: %s", blk, strerror(errno));
69 	else if (err != QT_BLKSIZE)
70 		memset(buf + err, 0, QT_BLKSIZE - err);
71 }
72 
73 /* Write block */
write_blk(struct quota_handle * h,uint blk,dqbuf_t buf)74 static int write_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
75 {
76 	int err;
77 
78 	err = h->e2fs_write(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
79 			QT_BLKSIZE);
80 	if (err < 0 && errno != ENOSPC)
81 		log_err("Cannot write block (%u): %s", blk, strerror(errno));
82 	if (err != QT_BLKSIZE)
83 		return -ENOSPC;
84 	return 0;
85 }
86 
87 /* Get free block in file (either from free list or create new one) */
get_free_dqblk(struct quota_handle * h)88 static int get_free_dqblk(struct quota_handle *h)
89 {
90 	dqbuf_t buf = getdqbuf();
91 	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
92 	struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
93 	int blk;
94 
95 	if (!buf)
96 		return -ENOMEM;
97 
98 	if (info->dqi_free_blk) {
99 		blk = info->dqi_free_blk;
100 		read_blk(h, blk, buf);
101 		info->dqi_free_blk = ext2fs_le32_to_cpu(dh->dqdh_next_free);
102 	} else {
103 		memset(buf, 0, QT_BLKSIZE);
104 		/* Assure block allocation... */
105 		if (write_blk(h, info->dqi_blocks, buf) < 0) {
106 			freedqbuf(buf);
107 			log_err("Cannot allocate new quota block "
108 				"(out of disk space).");
109 			return -ENOSPC;
110 		}
111 		blk = info->dqi_blocks++;
112 	}
113 	mark_quotafile_info_dirty(h);
114 	freedqbuf(buf);
115 	return blk;
116 }
117 
118 /* Put given block to free list */
put_free_dqblk(struct quota_handle * h,dqbuf_t buf,uint blk)119 static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf, uint blk)
120 {
121 	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
122 	struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
123 
124 	dh->dqdh_next_free = ext2fs_cpu_to_le32(info->dqi_free_blk);
125 	dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
126 	dh->dqdh_entries = ext2fs_cpu_to_le16(0);
127 	info->dqi_free_blk = blk;
128 	mark_quotafile_info_dirty(h);
129 	write_blk(h, blk, buf);
130 }
131 
132 /* Remove given block from the list of blocks with free entries */
remove_free_dqentry(struct quota_handle * h,dqbuf_t buf,uint blk)133 static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
134 {
135 	dqbuf_t tmpbuf = getdqbuf();
136 	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
137 	uint nextblk = ext2fs_le32_to_cpu(dh->dqdh_next_free), prevblk =
138 
139 		ext2fs_le32_to_cpu(dh->dqdh_prev_free);
140 
141 	if (!tmpbuf)
142 		return;
143 
144 	if (nextblk) {
145 		read_blk(h, nextblk, tmpbuf);
146 		((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
147 				dh->dqdh_prev_free;
148 		write_blk(h, nextblk, tmpbuf);
149 	}
150 	if (prevblk) {
151 		read_blk(h, prevblk, tmpbuf);
152 		((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
153 				dh->dqdh_next_free;
154 		write_blk(h, prevblk, tmpbuf);
155 	} else {
156 		h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk;
157 		mark_quotafile_info_dirty(h);
158 	}
159 	freedqbuf(tmpbuf);
160 	dh->dqdh_next_free = dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
161 	write_blk(h, blk, buf);	/* No matter whether write succeeds
162 				 * block is out of list */
163 }
164 
165 /* Insert given block to the beginning of list with free entries */
insert_free_dqentry(struct quota_handle * h,dqbuf_t buf,uint blk)166 static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
167 {
168 	dqbuf_t tmpbuf = getdqbuf();
169 	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
170 	struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
171 
172 	if (!tmpbuf)
173 		return;
174 
175 	dh->dqdh_next_free = ext2fs_cpu_to_le32(info->dqi_free_entry);
176 	dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
177 	write_blk(h, blk, buf);
178 	if (info->dqi_free_entry) {
179 		read_blk(h, info->dqi_free_entry, tmpbuf);
180 		((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
181 				ext2fs_cpu_to_le32(blk);
182 		write_blk(h, info->dqi_free_entry, tmpbuf);
183 	}
184 	freedqbuf(tmpbuf);
185 	info->dqi_free_entry = blk;
186 	mark_quotafile_info_dirty(h);
187 }
188 
189 /* Find space for dquot */
find_free_dqentry(struct quota_handle * h,struct dquot * dquot,int * err)190 static uint find_free_dqentry(struct quota_handle *h, struct dquot *dquot,
191 			      int *err)
192 {
193 	int blk, i;
194 	struct qt_disk_dqdbheader *dh;
195 	struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
196 	char *ddquot;
197 	dqbuf_t buf;
198 
199 	*err = 0;
200 	buf = getdqbuf();
201 	if (!buf) {
202 		*err = -ENOMEM;
203 		return 0;
204 	}
205 
206 	dh = (struct qt_disk_dqdbheader *)buf;
207 	if (info->dqi_free_entry) {
208 		blk = info->dqi_free_entry;
209 		read_blk(h, blk, buf);
210 	} else {
211 		blk = get_free_dqblk(h);
212 		if (blk < 0) {
213 			freedqbuf(buf);
214 			*err = blk;
215 			return 0;
216 		}
217 		memset(buf, 0, QT_BLKSIZE);
218 		info->dqi_free_entry = blk;
219 		mark_quotafile_info_dirty(h);
220 	}
221 
222 	/* Block will be full? */
223 	if (ext2fs_le16_to_cpu(dh->dqdh_entries) + 1 >=
224 	    qtree_dqstr_in_blk(info))
225 		remove_free_dqentry(h, buf, blk);
226 
227 	dh->dqdh_entries =
228 		ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) + 1);
229 	/* Find free structure in block */
230 	ddquot = buf + sizeof(struct qt_disk_dqdbheader);
231 	for (i = 0;
232 	     i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot);
233 	     i++)
234 		ddquot += info->dqi_entry_size;
235 
236 	if (i == qtree_dqstr_in_blk(info))
237 		log_err("find_free_dqentry(): Data block full unexpectedly.");
238 
239 	write_blk(h, blk, buf);
240 	dquot->dq_dqb.u.v2_mdqb.dqb_off =
241 		(blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
242 		i * info->dqi_entry_size;
243 	freedqbuf(buf);
244 	return blk;
245 }
246 
247 /* Insert reference to structure into the trie */
do_insert_tree(struct quota_handle * h,struct dquot * dquot,uint * treeblk,int depth)248 static int do_insert_tree(struct quota_handle *h, struct dquot *dquot,
249 			  uint * treeblk, int depth)
250 {
251 	dqbuf_t buf;
252 	int newson = 0, newact = 0;
253 	u_int32_t *ref;
254 	uint newblk;
255 	int ret = 0;
256 
257 	log_debug("inserting in tree: treeblk=%u, depth=%d", *treeblk, depth);
258 	buf = getdqbuf();
259 	if (!buf)
260 		return -ENOMEM;
261 
262 	if (!*treeblk) {
263 		ret = get_free_dqblk(h);
264 		if (ret < 0)
265 			goto out_buf;
266 		*treeblk = ret;
267 		memset(buf, 0, QT_BLKSIZE);
268 		newact = 1;
269 	} else {
270 		read_blk(h, *treeblk, buf);
271 	}
272 
273 	ref = (u_int32_t *) buf;
274 	newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
275 	if (!newblk)
276 		newson = 1;
277 	if (depth == QT_TREEDEPTH - 1) {
278 		if (newblk)
279 			log_err("Inserting already present quota entry "
280 				"(block %u).",
281 				ref[get_index(dquot->dq_id, depth)]);
282 		newblk = find_free_dqentry(h, dquot, &ret);
283 	} else {
284 		ret = do_insert_tree(h, dquot, &newblk, depth + 1);
285 	}
286 
287 	if (newson && ret >= 0) {
288 		ref[get_index(dquot->dq_id, depth)] =
289 			ext2fs_cpu_to_le32(newblk);
290 		write_blk(h, *treeblk, buf);
291 	} else if (newact && ret < 0) {
292 		put_free_dqblk(h, buf, *treeblk);
293 	}
294 
295 out_buf:
296 	freedqbuf(buf);
297 	return ret;
298 }
299 
300 /* Wrapper for inserting quota structure into tree */
dq_insert_tree(struct quota_handle * h,struct dquot * dquot)301 static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot)
302 {
303 	uint tmp = QT_TREEOFF;
304 
305 	if (do_insert_tree(h, dquot, &tmp, 0) < 0)
306 		log_err("Cannot write quota (id %u): %s",
307 			(uint) dquot->dq_id, strerror(errno));
308 }
309 
310 /* Write dquot to file */
qtree_write_dquot(struct dquot * dquot)311 void qtree_write_dquot(struct dquot *dquot)
312 {
313 	ssize_t ret;
314 	char *ddquot;
315 	struct quota_handle *h = dquot->dq_h;
316 	struct qtree_mem_dqinfo *info =
317 			&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
318 	log_debug("writing ddquot 1: off=%llu, info->dqi_entry_size=%u",
319 			dquot->dq_dqb.u.v2_mdqb.dqb_off,
320 			info->dqi_entry_size);
321 	ret = ext2fs_get_mem(info->dqi_entry_size, &ddquot);
322 	if (ret) {
323 		errno = ENOMEM;
324 		log_err("Quota write failed (id %u): %s",
325 			(uint)dquot->dq_id, strerror(errno));
326 		return;
327 	}
328 
329 	if (!dquot->dq_dqb.u.v2_mdqb.dqb_off)
330 		dq_insert_tree(dquot->dq_h, dquot);
331 	info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
332 	log_debug("writing ddquot 2: off=%llu, info->dqi_entry_size=%u",
333 			dquot->dq_dqb.u.v2_mdqb.dqb_off,
334 			info->dqi_entry_size);
335 	ret = h->e2fs_write(&h->qh_qf, dquot->dq_dqb.u.v2_mdqb.dqb_off, ddquot,
336 			info->dqi_entry_size);
337 
338 	if (ret != info->dqi_entry_size) {
339 		if (ret > 0)
340 			errno = ENOSPC;
341 		log_err("Quota write failed (id %u): %s",
342 			(uint)dquot->dq_id, strerror(errno));
343 	}
344 	ext2fs_free_mem(&ddquot);
345 }
346 
347 /* Free dquot entry in data block */
free_dqentry(struct quota_handle * h,struct dquot * dquot,uint blk)348 static void free_dqentry(struct quota_handle *h, struct dquot *dquot, uint blk)
349 {
350 	struct qt_disk_dqdbheader *dh;
351 	struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
352 	dqbuf_t buf = getdqbuf();
353 
354 	if (!buf)
355 		return;
356 
357 	if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk)
358 		log_err("Quota structure has offset to other block (%u) "
359 			"than it should (%u).", blk,
360 			  (uint) (dquot->dq_dqb.u.v2_mdqb.dqb_off >>
361 				  QT_BLKSIZE_BITS));
362 
363 	read_blk(h, blk, buf);
364 	dh = (struct qt_disk_dqdbheader *)buf;
365 	dh->dqdh_entries =
366 		ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) - 1);
367 
368 	if (!ext2fs_le16_to_cpu(dh->dqdh_entries)) {	/* Block got free? */
369 		remove_free_dqentry(h, buf, blk);
370 		put_free_dqblk(h, buf, blk);
371 	} else {
372 		memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off &
373 			      ((1 << QT_BLKSIZE_BITS) - 1)),
374 		       0, info->dqi_entry_size);
375 
376 		/* First free entry? */
377 		if (ext2fs_le16_to_cpu(dh->dqdh_entries) ==
378 				qtree_dqstr_in_blk(info) - 1)
379 			/* This will also write data block */
380 			insert_free_dqentry(h, buf, blk);
381 		else
382 			write_blk(h, blk, buf);
383 	}
384 	dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
385 	freedqbuf(buf);
386 }
387 
388 /* Remove reference to dquot from tree */
remove_tree(struct quota_handle * h,struct dquot * dquot,uint * blk,int depth)389 static void remove_tree(struct quota_handle *h, struct dquot *dquot,
390 			uint * blk, int depth)
391 {
392 	dqbuf_t buf = getdqbuf();
393 	uint newblk;
394 	u_int32_t *ref = (u_int32_t *) buf;
395 
396 	if (!buf)
397 		return;
398 
399 	read_blk(h, *blk, buf);
400 	newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
401 	if (depth == QT_TREEDEPTH - 1) {
402 		free_dqentry(h, dquot, newblk);
403 		newblk = 0;
404 	} else {
405 		remove_tree(h, dquot, &newblk, depth + 1);
406 	}
407 
408 	if (!newblk) {
409 		int i;
410 
411 		ref[get_index(dquot->dq_id, depth)] = ext2fs_cpu_to_le32(0);
412 
413 		/* Block got empty? */
414 		for (i = 0; i < QT_BLKSIZE && !buf[i]; i++);
415 
416 		/* Don't put the root block into the free block list */
417 		if (i == QT_BLKSIZE && *blk != QT_TREEOFF) {
418 			put_free_dqblk(h, buf, *blk);
419 			*blk = 0;
420 		} else {
421 			write_blk(h, *blk, buf);
422 		}
423 	}
424 	freedqbuf(buf);
425 }
426 
427 /* Delete dquot from tree */
qtree_delete_dquot(struct dquot * dquot)428 void qtree_delete_dquot(struct dquot *dquot)
429 {
430 	uint tmp = QT_TREEOFF;
431 
432 	if (!dquot->dq_dqb.u.v2_mdqb.dqb_off)	/* Even not allocated? */
433 		return;
434 	remove_tree(dquot->dq_h, dquot, &tmp, 0);
435 }
436 
437 /* Find entry in block */
find_block_dqentry(struct quota_handle * h,struct dquot * dquot,uint blk)438 static ext2_loff_t find_block_dqentry(struct quota_handle *h,
439 				      struct dquot *dquot, uint blk)
440 {
441 	struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
442 	dqbuf_t buf = getdqbuf();
443 	int i;
444 	char *ddquot = buf + sizeof(struct qt_disk_dqdbheader);
445 
446 	if (!buf)
447 		return -ENOMEM;
448 
449 	read_blk(h, blk, buf);
450 	for (i = 0;
451 	     i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot);
452 	     i++)
453 		ddquot += info->dqi_entry_size;
454 
455 	if (i == qtree_dqstr_in_blk(info))
456 		log_err("Quota for id %u referenced but not present.",
457 			dquot->dq_id);
458 	freedqbuf(buf);
459 	return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
460 		i * info->dqi_entry_size;
461 }
462 
463 /* Find entry for given id in the tree */
find_tree_dqentry(struct quota_handle * h,struct dquot * dquot,uint blk,int depth)464 static ext2_loff_t find_tree_dqentry(struct quota_handle *h,
465 				     struct dquot *dquot,
466 				     uint blk, int depth)
467 {
468 	dqbuf_t buf = getdqbuf();
469 	ext2_loff_t ret = 0;
470 	u_int32_t *ref = (u_int32_t *) buf;
471 
472 	if (!buf)
473 		return -ENOMEM;
474 
475 	read_blk(h, blk, buf);
476 	ret = 0;
477 	blk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
478 	if (!blk)	/* No reference? */
479 		goto out_buf;
480 	if (depth < QT_TREEDEPTH - 1)
481 		ret = find_tree_dqentry(h, dquot, blk, depth + 1);
482 	else
483 		ret = find_block_dqentry(h, dquot, blk);
484 out_buf:
485 	freedqbuf(buf);
486 	return ret;
487 }
488 
489 /* Find entry for given id in the tree - wrapper function */
find_dqentry(struct quota_handle * h,struct dquot * dquot)490 static inline ext2_loff_t find_dqentry(struct quota_handle *h,
491 				       struct dquot *dquot)
492 {
493 	return find_tree_dqentry(h, dquot, QT_TREEOFF, 0);
494 }
495 
496 /*
497  *  Read dquot from disk.
498  */
qtree_read_dquot(struct quota_handle * h,qid_t id)499 struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
500 {
501 	struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
502 	ext2_loff_t offset;
503 	ssize_t ret;
504 	char *ddquot;
505 	struct dquot *dquot = get_empty_dquot();
506 
507 	if (!dquot)
508 		return NULL;
509 	if (ext2fs_get_mem(info->dqi_entry_size, &ddquot)) {
510 		ext2fs_free_mem(&dquot);
511 		return NULL;
512 	}
513 
514 	dquot->dq_id = id;
515 	dquot->dq_h = h;
516 	dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
517 	memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
518 
519 	offset = find_dqentry(h, dquot);
520 	if (offset > 0) {
521 		dquot->dq_dqb.u.v2_mdqb.dqb_off = offset;
522 		ret = h->e2fs_read(&h->qh_qf, offset, ddquot,
523 			info->dqi_entry_size);
524 		if (ret != info->dqi_entry_size) {
525 			if (ret > 0)
526 				errno = EIO;
527 			log_err("Cannot read quota structure for id %u: %s",
528 				dquot->dq_id, strerror(errno));
529 		}
530 		info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
531 	}
532 	ext2fs_free_mem(&ddquot);
533 	return dquot;
534 }
535 
536 /*
537  * Scan all dquots in file and call callback on each
538  */
539 #define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
540 #define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
541 
report_block(struct dquot * dquot,uint blk,char * bitmap,int (* process_dquot)(struct dquot *,void *),void * data)542 static int report_block(struct dquot *dquot, uint blk, char *bitmap,
543 			int (*process_dquot) (struct dquot *, void *),
544 			void *data)
545 {
546 	struct qtree_mem_dqinfo *info =
547 			&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
548 	dqbuf_t buf = getdqbuf();
549 	struct qt_disk_dqdbheader *dh;
550 	char *ddata;
551 	int entries, i;
552 
553 	if (!buf)
554 		return 0;
555 
556 	set_bit(bitmap, blk);
557 	read_blk(dquot->dq_h, blk, buf);
558 	dh = (struct qt_disk_dqdbheader *)buf;
559 	ddata = buf + sizeof(struct qt_disk_dqdbheader);
560 	entries = ext2fs_le16_to_cpu(dh->dqdh_entries);
561 	for (i = 0; i < qtree_dqstr_in_blk(info);
562 			i++, ddata += info->dqi_entry_size)
563 		if (!qtree_entry_unused(info, ddata)) {
564 			dquot->dq_dqb.u.v2_mdqb.dqb_off =
565 				(blk << QT_BLKSIZE_BITS) +
566 				sizeof(struct qt_disk_dqdbheader) +
567 				i * info->dqi_entry_size;
568 			info->dqi_ops->disk2mem_dqblk(dquot, ddata);
569 			if (process_dquot(dquot, data) < 0)
570 				break;
571 		}
572 	freedqbuf(buf);
573 	return entries;
574 }
575 
check_reference(struct quota_handle * h,uint blk)576 static void check_reference(struct quota_handle *h, uint blk)
577 {
578 	if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks)
579 		log_err("Illegal reference (%u >= %u) in %s quota file. "
580 			"Quota file is probably corrupted.\n"
581 			"Please run e2fsck (8) to fix it.",
582 			blk,
583 			h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
584 			type2name(h->qh_type));
585 }
586 
report_tree(struct dquot * dquot,uint blk,int depth,char * bitmap,int (* process_dquot)(struct dquot *,void *),void * data)587 static int report_tree(struct dquot *dquot, uint blk, int depth, char *bitmap,
588 		       int (*process_dquot) (struct dquot *, void *),
589 		       void *data)
590 {
591 	int entries = 0, i;
592 	dqbuf_t buf = getdqbuf();
593 	u_int32_t *ref = (u_int32_t *) buf;
594 
595 	if (!buf)
596 		return 0;
597 
598 	read_blk(dquot->dq_h, blk, buf);
599 	if (depth == QT_TREEDEPTH - 1) {
600 		for (i = 0; i < QT_BLKSIZE >> 2; i++) {
601 			blk = ext2fs_le32_to_cpu(ref[i]);
602 			check_reference(dquot->dq_h, blk);
603 			if (blk && !get_bit(bitmap, blk))
604 				entries += report_block(dquot, blk, bitmap,
605 							process_dquot, data);
606 		}
607 	} else {
608 		for (i = 0; i < QT_BLKSIZE >> 2; i++) {
609 			blk = ext2fs_le32_to_cpu(ref[i]);
610 			if (blk) {
611 				check_reference(dquot->dq_h, blk);
612 				entries += report_tree(dquot, blk, depth + 1,
613 						       bitmap, process_dquot,
614 						       data);
615 			}
616 		}
617 	}
618 	freedqbuf(buf);
619 	return entries;
620 }
621 
find_set_bits(char * bmp,int blocks)622 static uint find_set_bits(char *bmp, int blocks)
623 {
624 	uint i, used = 0;
625 
626 	for (i = 0; i < blocks; i++)
627 		if (get_bit(bmp, i))
628 			used++;
629 	return used;
630 }
631 
qtree_scan_dquots(struct quota_handle * h,int (* process_dquot)(struct dquot *,void *),void * data)632 int qtree_scan_dquots(struct quota_handle *h,
633 		      int (*process_dquot) (struct dquot *, void *),
634 		      void *data)
635 {
636 	char *bitmap;
637 	struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
638 	struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
639 	struct dquot *dquot = get_empty_dquot();
640 
641 	if (!dquot)
642 		return -1;
643 
644 	dquot->dq_h = h;
645 	if (ext2fs_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap)) {
646 		ext2fs_free_mem(&dquot);
647 		return -1;
648 	}
649 	v2info->dqi_used_entries = report_tree(dquot, QT_TREEOFF, 0, bitmap,
650 					       process_dquot, data);
651 	v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
652 	ext2fs_free_mem(&bitmap);
653 	ext2fs_free_mem(&dquot);
654 	return 0;
655 }
656