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