Home | History | Annotate | Download | only in support
      1 /*
      2  * mkquota.c --- create quota files for a filesystem
      3  *
      4  * Aditya Kali <adityakali (at) 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
     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 
     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
     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  */
     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  */
     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 
    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 
    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 
    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 
    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 
    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 
    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  */
    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 
    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 
    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  */
    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  */
    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  */
    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 
    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 
    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  */
    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  */
    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  */
    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 
    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