1 /*
2  * mkquota.c --- create quota files for a filesystem
3  *
4  * Aditya Kali <adityakali@google.com>
5  */
6 #include "config.h"
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <fcntl.h>
13 
14 #include "ext2fs/ext2_fs.h"
15 #include "ext2fs/ext2fs.h"
16 #include "e2p/e2p.h"
17 
18 #include "quotaio.h"
19 #include "quotaio_v2.h"
20 #include "quotaio_tree.h"
21 #include "common.h"
22 #include "dict.h"
23 
24 /* Needed for architectures where sizeof(int) != sizeof(void *) */
25 #define UINT_TO_VOIDPTR(val)  ((void *)(intptr_t)(val))
26 #define VOIDPTR_TO_UINT(ptr)  ((unsigned int)(intptr_t)(ptr))
27 
28 #if DEBUG_QUOTA
print_inode(struct ext2_inode * inode)29 static void print_inode(struct ext2_inode *inode)
30 {
31 	if (!inode)
32 		return;
33 
34 	fprintf(stderr, "  i_mode = %d\n", inode->i_mode);
35 	fprintf(stderr, "  i_uid = %d\n", inode->i_uid);
36 	fprintf(stderr, "  i_size = %d\n", inode->i_size);
37 	fprintf(stderr, "  i_atime = %d\n", inode->i_atime);
38 	fprintf(stderr, "  i_ctime = %d\n", inode->i_ctime);
39 	fprintf(stderr, "  i_mtime = %d\n", inode->i_mtime);
40 	fprintf(stderr, "  i_dtime = %d\n", inode->i_dtime);
41 	fprintf(stderr, "  i_gid = %d\n", inode->i_gid);
42 	fprintf(stderr, "  i_links_count = %d\n", inode->i_links_count);
43 	fprintf(stderr, "  i_blocks = %d\n", inode->i_blocks);
44 	fprintf(stderr, "  i_flags = %d\n", inode->i_flags);
45 
46 	return;
47 }
48 
print_dquot(const char * desc,struct dquot * dq)49 static void print_dquot(const char *desc, struct dquot *dq)
50 {
51 	if (desc)
52 		fprintf(stderr, "%s: ", desc);
53 	fprintf(stderr, "%u %lld:%lld:%lld %lld:%lld:%lld\n",
54 		dq->dq_id, dq->dq_dqb.dqb_curspace,
55 		dq->dq_dqb.dqb_bsoftlimit, dq->dq_dqb.dqb_bhardlimit,
56 		dq->dq_dqb.dqb_curinodes,
57 		dq->dq_dqb.dqb_isoftlimit, dq->dq_dqb.dqb_ihardlimit);
58 }
59 #else
print_dquot(const char * desc EXT2FS_ATTR ((unused)),struct dquot * dq EXT2FS_ATTR ((unused)))60 static void print_dquot(const char *desc EXT2FS_ATTR((unused)),
61 			struct dquot *dq EXT2FS_ATTR((unused)))
62 {
63 }
64 #endif
65 
66 /*
67  * Returns 0 if not able to find the quota file, otherwise returns its
68  * inode number.
69  */
quota_file_exists(ext2_filsys fs,enum quota_type qtype)70 int quota_file_exists(ext2_filsys fs, enum quota_type qtype)
71 {
72 	char qf_name[256];
73 	errcode_t ret;
74 	ext2_ino_t ino;
75 
76 	if (qtype >= MAXQUOTAS)
77 		return -EINVAL;
78 
79 	quota_get_qf_name(qtype, QFMT_VFS_V1, qf_name);
80 
81 	ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0,
82 			    &ino);
83 	if (ret)
84 		return 0;
85 
86 	return ino;
87 }
88 
89 /*
90  * Set the value for reserved quota inode number field in superblock.
91  */
quota_set_sb_inum(ext2_filsys fs,ext2_ino_t ino,enum quota_type qtype)92 void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, enum quota_type qtype)
93 {
94 	ext2_ino_t *inump;
95 
96 	inump = quota_sb_inump(fs->super, qtype);
97 
98 	log_debug("setting quota ino in superblock: ino=%u, type=%d", ino,
99 		 qtype);
100 	*inump = ino;
101 	ext2fs_mark_super_dirty(fs);
102 }
103 
quota_remove_inode(ext2_filsys fs,enum quota_type qtype)104 errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype)
105 {
106 	ext2_ino_t qf_ino;
107 	errcode_t	retval;
108 
109 	retval = ext2fs_read_bitmaps(fs);
110 	if (retval) {
111 		log_debug("Couldn't read bitmaps: %s", error_message(retval));
112 		return retval;
113 	}
114 
115 	qf_ino = *quota_sb_inump(fs->super, qtype);
116 	if (qf_ino == 0)
117 		return 0;
118 	retval = quota_inode_truncate(fs, qf_ino);
119 	if (retval)
120 		return retval;
121 	if (qf_ino >= EXT2_FIRST_INODE(fs->super)) {
122 		struct ext2_inode inode;
123 
124 		retval = ext2fs_read_inode(fs, qf_ino, &inode);
125 		if (!retval) {
126 			memset(&inode, 0, sizeof(struct ext2_inode));
127 			ext2fs_write_inode(fs, qf_ino, &inode);
128 		}
129 		ext2fs_inode_alloc_stats2(fs, qf_ino, -1, 0);
130 		ext2fs_mark_ib_dirty(fs);
131 
132 	}
133 	quota_set_sb_inum(fs, 0, qtype);
134 
135 	ext2fs_mark_super_dirty(fs);
136 	fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
137 	retval = ext2fs_write_bitmaps(fs);
138 	if (retval) {
139 		log_debug("Couldn't write bitmaps: %s", error_message(retval));
140 		return retval;
141 	}
142 	return 0;
143 }
144 
write_dquots(dict_t * dict,struct quota_handle * qh)145 static void write_dquots(dict_t *dict, struct quota_handle *qh)
146 {
147 	dnode_t		*n;
148 	struct dquot	*dq;
149 
150 	for (n = dict_first(dict); n; n = dict_next(dict, n)) {
151 		dq = dnode_get(n);
152 		if (dq) {
153 			print_dquot("write", dq);
154 			dq->dq_h = qh;
155 			update_grace_times(dq);
156 			qh->qh_ops->commit_dquot(dq);
157 		}
158 	}
159 }
160 
quota_write_inode(quota_ctx_t qctx,unsigned int qtype_bits)161 errcode_t quota_write_inode(quota_ctx_t qctx, unsigned int qtype_bits)
162 {
163 	int		retval = 0;
164 	enum quota_type	qtype;
165 	dict_t		*dict;
166 	ext2_filsys	fs;
167 	struct quota_handle *h = NULL;
168 	int		fmt = QFMT_VFS_V1;
169 
170 	if (!qctx)
171 		return 0;
172 
173 	fs = qctx->fs;
174 	retval = ext2fs_get_mem(sizeof(struct quota_handle), &h);
175 	if (retval) {
176 		log_debug("Unable to allocate quota handle: %s",
177 			error_message(retval));
178 		goto out;
179 	}
180 
181 	retval = ext2fs_read_bitmaps(fs);
182 	if (retval) {
183 		log_debug("Couldn't read bitmaps: %s", error_message(retval));
184 		goto out;
185 	}
186 
187 	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
188 		if (((1 << qtype) & qtype_bits) == 0)
189 			continue;
190 
191 		dict = qctx->quota_dict[qtype];
192 		if (!dict)
193 			continue;
194 
195 		retval = quota_file_create(h, fs, qtype, fmt);
196 		if (retval < 0) {
197 			log_debug("Cannot initialize io on quotafile");
198 			continue;
199 		}
200 
201 		write_dquots(dict, h);
202 		retval = quota_file_close(qctx, h);
203 		if (retval < 0) {
204 			log_err("Cannot finish IO on new quotafile: %s",
205 				strerror(errno));
206 			if (h->qh_qf.e2_file)
207 				ext2fs_file_close(h->qh_qf.e2_file);
208 			(void) quota_inode_truncate(fs, h->qh_qf.ino);
209 			continue;
210 		}
211 
212 		/* Set quota inode numbers in superblock. */
213 		quota_set_sb_inum(fs, h->qh_qf.ino, qtype);
214 		ext2fs_mark_super_dirty(fs);
215 		ext2fs_mark_bb_dirty(fs);
216 		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
217 	}
218 
219 	retval = ext2fs_write_bitmaps(fs);
220 	if (retval) {
221 		log_debug("Couldn't write bitmaps: %s", error_message(retval));
222 		goto out;
223 	}
224 out:
225 	if (h)
226 		ext2fs_free_mem(&h);
227 	return retval;
228 }
229 
230 /******************************************************************/
231 /* Helper functions for computing quota in memory.                */
232 /******************************************************************/
233 
dict_uint_cmp(const void * a,const void * b)234 static int dict_uint_cmp(const void *a, const void *b)
235 {
236 	unsigned int	c, d;
237 
238 	c = VOIDPTR_TO_UINT(a);
239 	d = VOIDPTR_TO_UINT(b);
240 
241 	if (c == d)
242 		return 0;
243 	else if (c > d)
244 		return 1;
245 	else
246 		return -1;
247 }
248 
get_qid(struct ext2_inode_large * inode,enum quota_type qtype)249 static inline qid_t get_qid(struct ext2_inode_large *inode, enum quota_type qtype)
250 {
251 	unsigned int inode_size;
252 
253 	switch (qtype) {
254 	case USRQUOTA:
255 		return inode_uid(*inode);
256 	case GRPQUOTA:
257 		return inode_gid(*inode);
258 	case PRJQUOTA:
259 		inode_size = EXT2_GOOD_OLD_INODE_SIZE +
260 			inode->i_extra_isize;
261 		if (inode_includes(inode_size, i_projid))
262 			return inode_projid(*inode);
263 	default:
264 		return 0;
265 	}
266 
267 	return 0;
268 }
269 
quota_dnode_free(dnode_t * node,void * context EXT2FS_ATTR ((unused)))270 static void quota_dnode_free(dnode_t *node,
271 			     void *context EXT2FS_ATTR((unused)))
272 {
273 	void *ptr = node ? dnode_get(node) : 0;
274 
275 	ext2fs_free_mem(&ptr);
276 	free(node);
277 }
278 
279 /*
280  * Set up the quota tracking data structures.
281  */
quota_init_context(quota_ctx_t * qctx,ext2_filsys fs,unsigned int qtype_bits)282 errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs,
283 			     unsigned int qtype_bits)
284 {
285 	errcode_t err;
286 	dict_t	*dict;
287 	quota_ctx_t ctx;
288 	enum quota_type	qtype;
289 
290 	err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx);
291 	if (err) {
292 		log_debug("Failed to allocate quota context");
293 		return err;
294 	}
295 
296 	memset(ctx, 0, sizeof(struct quota_ctx));
297 	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
298 		ctx->quota_file[qtype] = NULL;
299 		if (((1 << qtype) & qtype_bits) == 0)
300 			continue;
301 		err = ext2fs_get_mem(sizeof(dict_t), &dict);
302 		if (err) {
303 			log_debug("Failed to allocate dictionary");
304 			quota_release_context(&ctx);
305 			return err;
306 		}
307 		ctx->quota_dict[qtype] = dict;
308 		dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp);
309 		dict_set_allocator(dict, NULL, quota_dnode_free, NULL);
310 	}
311 
312 	ctx->fs = fs;
313 	*qctx = ctx;
314 	return 0;
315 }
316 
quota_release_context(quota_ctx_t * qctx)317 void quota_release_context(quota_ctx_t *qctx)
318 {
319 	errcode_t err;
320 	dict_t	*dict;
321 	enum quota_type	qtype;
322 	quota_ctx_t ctx;
323 
324 	if (!qctx)
325 		return;
326 
327 	ctx = *qctx;
328 	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
329 		dict = ctx->quota_dict[qtype];
330 		ctx->quota_dict[qtype] = 0;
331 		if (dict) {
332 			dict_free_nodes(dict);
333 			free(dict);
334 		}
335 		if (ctx->quota_file[qtype]) {
336 			err = quota_file_close(ctx, ctx->quota_file[qtype]);
337 			if (err) {
338 				log_err("Cannot close quotafile: %s",
339 					strerror(errno));
340 				ext2fs_free_mem(&ctx->quota_file[qtype]);
341 			}
342 		}
343 	}
344 	*qctx = NULL;
345 	free(ctx);
346 }
347 
get_dq(dict_t * dict,__u32 key)348 static struct dquot *get_dq(dict_t *dict, __u32 key)
349 {
350 	struct dquot	*dq;
351 	dnode_t		*n;
352 
353 	n = dict_lookup(dict, UINT_TO_VOIDPTR(key));
354 	if (n)
355 		dq = dnode_get(n);
356 	else {
357 		if (ext2fs_get_mem(sizeof(struct dquot), &dq)) {
358 			log_err("Unable to allocate dquot");
359 			return NULL;
360 		}
361 		memset(dq, 0, sizeof(struct dquot));
362 		dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq);
363 		dq->dq_id = key;
364 	}
365 	return dq;
366 }
367 
368 
369 /*
370  * Called to update the blocks used by a particular inode
371  */
quota_data_add(quota_ctx_t qctx,struct ext2_inode_large * inode,ext2_ino_t ino EXT2FS_ATTR ((unused)),qsize_t space)372 void quota_data_add(quota_ctx_t qctx, struct ext2_inode_large *inode,
373 		    ext2_ino_t ino EXT2FS_ATTR((unused)),
374 		    qsize_t space)
375 {
376 	struct dquot	*dq;
377 	dict_t		*dict;
378 	enum quota_type	qtype;
379 
380 	if (!qctx)
381 		return;
382 
383 	log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
384 			inode_uid(*inode),
385 			inode_gid(*inode), space);
386 	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
387 		dict = qctx->quota_dict[qtype];
388 		if (dict) {
389 			dq = get_dq(dict, get_qid(inode, qtype));
390 			if (dq)
391 				dq->dq_dqb.dqb_curspace += space;
392 		}
393 	}
394 }
395 
396 /*
397  * Called to remove some blocks used by a particular inode
398  */
quota_data_sub(quota_ctx_t qctx,struct ext2_inode_large * inode,ext2_ino_t ino EXT2FS_ATTR ((unused)),qsize_t space)399 void quota_data_sub(quota_ctx_t qctx, struct ext2_inode_large *inode,
400 		    ext2_ino_t ino EXT2FS_ATTR((unused)),
401 		    qsize_t space)
402 {
403 	struct dquot	*dq;
404 	dict_t		*dict;
405 	enum quota_type	qtype;
406 
407 	if (!qctx)
408 		return;
409 
410 	log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
411 			inode_uid(*inode),
412 			inode_gid(*inode), space);
413 	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
414 		dict = qctx->quota_dict[qtype];
415 		if (dict) {
416 			dq = get_dq(dict, get_qid(inode, qtype));
417 			dq->dq_dqb.dqb_curspace -= space;
418 		}
419 	}
420 }
421 
422 /*
423  * Called to count the files used by an inode's user/group
424  */
quota_data_inodes(quota_ctx_t qctx,struct ext2_inode_large * inode,ext2_ino_t ino EXT2FS_ATTR ((unused)),int adjust)425 void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode_large *inode,
426 		       ext2_ino_t ino EXT2FS_ATTR((unused)), int adjust)
427 {
428 	struct dquot	*dq;
429 	dict_t		*dict;
430 	enum quota_type	qtype;
431 
432 	if (!qctx)
433 		return;
434 
435 	log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino,
436 			inode_uid(*inode),
437 			inode_gid(*inode), adjust);
438 	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
439 		dict = qctx->quota_dict[qtype];
440 		if (dict) {
441 			dq = get_dq(dict, get_qid(inode, qtype));
442 			dq->dq_dqb.dqb_curinodes += adjust;
443 		}
444 	}
445 }
446 
quota_compute_usage(quota_ctx_t qctx)447 errcode_t quota_compute_usage(quota_ctx_t qctx)
448 {
449 	ext2_filsys fs;
450 	ext2_ino_t ino;
451 	errcode_t ret;
452 	struct ext2_inode_large *inode;
453 	int inode_size;
454 	qsize_t space;
455 	ext2_inode_scan scan;
456 
457 	if (!qctx)
458 		return 0;
459 
460 	fs = qctx->fs;
461 	ret = ext2fs_open_inode_scan(fs, 0, &scan);
462 	if (ret) {
463 		log_err("while opening inode scan. ret=%ld", ret);
464 		return ret;
465 	}
466 	inode_size = fs->super->s_inode_size;
467 	inode = malloc(inode_size);
468 	if (!inode)
469 		return ENOMEM;
470 	while (1) {
471 		ret = ext2fs_get_next_inode_full(scan, &ino,
472 						 EXT2_INODE(inode), inode_size);
473 		if (ret) {
474 			log_err("while getting next inode. ret=%ld", ret);
475 			ext2fs_close_inode_scan(scan);
476 			free(inode);
477 			return ret;
478 		}
479 		if (ino == 0)
480 			break;
481 		if (inode->i_links_count &&
482 		    (ino == EXT2_ROOT_INO ||
483 		     ino >= EXT2_FIRST_INODE(fs->super))) {
484 			space = ext2fs_inode_i_blocks(fs,
485 						      EXT2_INODE(inode)) << 9;
486 			quota_data_add(qctx, inode, ino, space);
487 			quota_data_inodes(qctx, inode, ino, +1);
488 		}
489 	}
490 
491 	ext2fs_close_inode_scan(scan);
492 	free(inode);
493 	return 0;
494 }
495 
496 struct scan_dquots_data {
497 	dict_t		*quota_dict;
498 	int             update_limits; /* update limits from disk */
499 	int		update_usage;
500 	int		usage_is_inconsistent;
501 };
502 
scan_dquots_callback(struct dquot * dquot,void * cb_data)503 static int scan_dquots_callback(struct dquot *dquot, void *cb_data)
504 {
505 	struct scan_dquots_data *scan_data = cb_data;
506 	dict_t *quota_dict = scan_data->quota_dict;
507 	struct dquot *dq;
508 
509 	dq = get_dq(quota_dict, dquot->dq_id);
510 	dq->dq_id = dquot->dq_id;
511 	dq->dq_flags |= DQF_SEEN;
512 
513 	print_dquot("mem", dq);
514 	print_dquot("dsk", dquot);
515 
516 	/* Check if there is inconsistancy. */
517 	if (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace ||
518 	    dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes) {
519 		scan_data->usage_is_inconsistent = 1;
520 		fprintf(stderr, "[QUOTA WARNING] Usage inconsistent for ID %d:"
521 			"actual (%llu, %llu) != expected (%llu, %llu)\n",
522 			dq->dq_id, (long long)dq->dq_dqb.dqb_curspace,
523 			(long long)dq->dq_dqb.dqb_curinodes,
524 			(long long)dquot->dq_dqb.dqb_curspace,
525 			(long long)dquot->dq_dqb.dqb_curinodes);
526 	}
527 
528 	if (scan_data->update_limits) {
529 		dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
530 		dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
531 		dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
532 		dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
533 	}
534 
535 	if (scan_data->update_usage) {
536 		dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace;
537 		dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes;
538 	}
539 
540 	return 0;
541 }
542 
543 /*
544  * Read all dquots from quota file into memory
545  */
quota_read_all_dquots(struct quota_handle * qh,quota_ctx_t qctx,int update_limits)546 static errcode_t quota_read_all_dquots(struct quota_handle *qh,
547                                        quota_ctx_t qctx, int update_limits)
548 {
549 	struct scan_dquots_data scan_data;
550 
551 	scan_data.quota_dict = qctx->quota_dict[qh->qh_type];
552 	scan_data.update_limits = update_limits;
553 	scan_data.update_usage = 0;
554 
555 	return qh->qh_ops->scan_dquots(qh, scan_dquots_callback, &scan_data);
556 }
557 
558 /*
559  * Write all memory dquots into quota file
560  */
561 #if 0 /* currently unused, but may be useful in the future? */
562 static errcode_t quota_write_all_dquots(struct quota_handle *qh,
563                                         quota_ctx_t qctx)
564 {
565 	errcode_t err;
566 
567 	err = ext2fs_read_bitmaps(qctx->fs);
568 	if (err)
569 		return err;
570 	write_dquots(qctx->quota_dict[qh->qh_type], qh);
571 	ext2fs_mark_bb_dirty(qctx->fs);
572 	qctx->fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
573 	ext2fs_write_bitmaps(qctx->fs);
574 	return 0;
575 }
576 #endif
577 
578 /*
579  * Updates the in-memory quota limits from the given quota inode.
580  */
quota_update_limits(quota_ctx_t qctx,ext2_ino_t qf_ino,enum quota_type qtype)581 errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino,
582 			      enum quota_type qtype)
583 {
584 	struct quota_handle *qh;
585 	errcode_t err;
586 
587 	if (!qctx)
588 		return 0;
589 
590 	err = ext2fs_get_mem(sizeof(struct quota_handle), &qh);
591 	if (err) {
592 		log_debug("Unable to allocate quota handle");
593 		return err;
594 	}
595 
596 	err = quota_file_open(qctx, qh, qf_ino, qtype, -1, 0);
597 	if (err) {
598 		log_debug("Open quota file failed");
599 		goto out;
600 	}
601 
602 	quota_read_all_dquots(qh, qctx, 1);
603 
604 	err = quota_file_close(qctx, qh);
605 	if (err) {
606 		log_debug("Cannot finish IO on new quotafile: %s",
607 			strerror(errno));
608 		if (qh->qh_qf.e2_file)
609 			ext2fs_file_close(qh->qh_qf.e2_file);
610 	}
611 out:
612 	ext2fs_free_mem(&qh);
613 	return err;
614 }
615 
616 /*
617  * Compares the measured quota in qctx->quota_dict with that in the quota inode
618  * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
619  * set to 1 if the supplied and on-disk quota usage values are not identical.
620  */
quota_compare_and_update(quota_ctx_t qctx,enum quota_type qtype,int * usage_inconsistent)621 errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
622 				   int *usage_inconsistent)
623 {
624 	struct quota_handle qh;
625 	struct scan_dquots_data scan_data;
626 	struct dquot *dq;
627 	dnode_t *n;
628 	dict_t *dict = qctx->quota_dict[qtype];
629 	errcode_t err = 0;
630 
631 	if (!dict)
632 		goto out;
633 
634 	err = quota_file_open(qctx, &qh, 0, qtype, -1, 0);
635 	if (err) {
636 		log_debug("Open quota file failed");
637 		goto out;
638 	}
639 
640 	scan_data.quota_dict = qctx->quota_dict[qtype];
641 	scan_data.update_limits = 1;
642 	scan_data.update_usage = 0;
643 	scan_data.usage_is_inconsistent = 0;
644 	err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
645 	if (err) {
646 		log_debug("Error scanning dquots");
647 		goto out_close_qh;
648 	}
649 
650 	for (n = dict_first(dict); n; n = dict_next(dict, n)) {
651 		dq = dnode_get(n);
652 		if (!dq)
653 			continue;
654 		if ((dq->dq_flags & DQF_SEEN) == 0) {
655 			fprintf(stderr, "[QUOTA WARNING] "
656 				"Missing quota entry ID %d\n", dq->dq_id);
657 			scan_data.usage_is_inconsistent = 1;
658 		}
659 	}
660 	*usage_inconsistent = scan_data.usage_is_inconsistent;
661 
662 out_close_qh:
663 	err = quota_file_close(qctx, &qh);
664 	if (err) {
665 		log_debug("Cannot close quotafile: %s", error_message(errno));
666 		if (qh.qh_qf.e2_file)
667 			ext2fs_file_close(qh.qh_qf.e2_file);
668 	}
669 out:
670 	return err;
671 }
672 
parse_quota_opts(const char * opts,int (* func)(char *))673 int parse_quota_opts(const char *opts, int (*func)(char *))
674 {
675 	char	*buf, *token, *next, *p;
676 	int	len;
677 	int	ret = 0;
678 
679 	len = strlen(opts);
680 	buf = malloc(len + 1);
681 	if (!buf) {
682 		fprintf(stderr,
683 			"Couldn't allocate memory to parse quota options!\n");
684 		return -ENOMEM;
685 	}
686 	strcpy(buf, opts);
687 	for (token = buf; token && *token; token = next) {
688 		p = strchr(token, ',');
689 		next = 0;
690 		if (p) {
691 			*p = 0;
692 			next = p + 1;
693 		}
694 		ret = func(token);
695 		if (ret)
696 			break;
697 	}
698 	free(buf);
699 	return ret;
700 }
701