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