mkquota.c revision 3b802e43d9276a13cbc75144087cbf017672ca6c
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 "quota.h"
19#include "quotaio.h"
20#include "quotaio_v2.h"
21#include "quotaio_tree.h"
22#include "mkquota.h"
23#include "common.h"
24
25/* Needed for architectures where sizeof(int) != sizeof(void *) */
26#define UINT_TO_VOIDPTR(val)  ((void *)(intptr_t)(val))
27#define VOIDPTR_TO_UINT(ptr)  ((unsigned int)(intptr_t)(ptr))
28
29static 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/*
50 * Returns 0 if not able to find the quota file, otherwise returns its
51 * inode number.
52 */
53int quota_file_exists(ext2_filsys fs, int qtype, int fmt)
54{
55	char qf_name[256];
56	errcode_t ret;
57	ext2_ino_t ino;
58
59	if (qtype >= MAXQUOTAS)
60		return -EINVAL;
61
62	quota_get_qf_name(qtype, fmt, qf_name);
63
64	ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0,
65			    &ino);
66	if (ret)
67		return 0;
68
69	return ino;
70}
71
72/*
73 * Set the value for reserved quota inode number field in superblock.
74 */
75void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype)
76{
77	ext2_ino_t *inump;
78
79	inump = (qtype == USRQUOTA) ? &fs->super->s_usr_quota_inum :
80		&fs->super->s_grp_quota_inum;
81
82	log_debug("setting quota ino in superblock: ino=%u, type=%d", ino,
83		 qtype);
84	*inump = ino;
85	ext2fs_mark_super_dirty(fs);
86}
87
88errcode_t quota_remove_inode(ext2_filsys fs, int qtype)
89{
90	ext2_ino_t qf_ino;
91
92	ext2fs_read_bitmaps(fs);
93	qf_ino = (qtype == USRQUOTA) ? fs->super->s_usr_quota_inum :
94		fs->super->s_grp_quota_inum;
95	quota_set_sb_inum(fs, 0, qtype);
96	/* Truncate the inode only if its a reserved one. */
97	if (qf_ino < EXT2_FIRST_INODE(fs->super))
98		quota_inode_truncate(fs, qf_ino);
99
100	ext2fs_mark_super_dirty(fs);
101	ext2fs_write_bitmaps(fs);
102	return 0;
103}
104
105static void write_dquots(dict_t *dict, struct quota_handle *qh)
106{
107	dnode_t		*n;
108	struct dquot	*dq;
109
110	for (n = dict_first(dict); n; n = dict_next(dict, n)) {
111		dq = dnode_get(n);
112		if (dq) {
113			dq->dq_h = qh;
114			update_grace_times(dq);
115			qh->qh_ops->commit_dquot(dq);
116		}
117	}
118}
119
120errcode_t quota_write_inode(quota_ctx_t qctx, int qtype)
121{
122	int		retval = 0, i;
123	dict_t		*dict;
124	ext2_filsys	fs;
125	struct quota_handle *h;
126	int		fmt = QFMT_VFS_V1;
127
128	if (!qctx)
129		return 0;
130
131	fs = qctx->fs;
132	retval = ext2fs_get_mem(sizeof(struct quota_handle), &h);
133	if (retval) {
134		log_err("Unable to allocate quota handle", "");
135		goto out;
136	}
137
138	ext2fs_read_bitmaps(fs);
139
140	for (i = 0; i < MAXQUOTAS; i++) {
141		if ((qtype != -1) && (i != qtype))
142			continue;
143
144		dict = qctx->quota_dict[i];
145		if (!dict)
146			continue;
147
148		retval = quota_file_create(h, fs, i, fmt);
149		if (retval < 0) {
150			log_err("Cannot initialize io on quotafile", "");
151			continue;
152		}
153
154		write_dquots(dict, h);
155		retval = quota_file_close(h);
156		if (retval < 0) {
157			log_err("Cannot finish IO on new quotafile: %s",
158				strerror(errno));
159			if (h->qh_qf.e2_file)
160				ext2fs_file_close(h->qh_qf.e2_file);
161			quota_inode_truncate(fs, h->qh_qf.ino);
162			continue;
163		}
164
165		/* Set quota inode numbers in superblock. */
166		quota_set_sb_inum(fs, h->qh_qf.ino, i);
167		ext2fs_mark_super_dirty(fs);
168		ext2fs_mark_bb_dirty(fs);
169		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
170	}
171
172	ext2fs_write_bitmaps(fs);
173out:
174	if (h)
175		ext2fs_free_mem(&h);
176	return retval;
177}
178
179/******************************************************************/
180/* Helper functions for computing quota in memory.                */
181/******************************************************************/
182
183static int dict_uint_cmp(const void *a, const void *b)
184{
185	unsigned int	c, d;
186
187	c = VOIDPTR_TO_UINT(a);
188	d = VOIDPTR_TO_UINT(b);
189
190	return c - d;
191}
192
193static inline qid_t get_qid(struct ext2_inode *inode, int qtype)
194{
195	if (qtype == USRQUOTA)
196		return inode_uid(*inode);
197	return inode_gid(*inode);
198}
199
200static void quota_dnode_free(dnode_t *node,
201			     void *context EXT2FS_ATTR((unused)))
202{
203	void *ptr = node ? dnode_get(node) : 0;
204
205	ext2fs_free_mem(&ptr);
206	free(node);
207}
208
209/*
210 * Set up the quota tracking data structures.
211 */
212errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype)
213{
214	int	i, err = 0;
215	dict_t	*dict;
216	quota_ctx_t ctx;
217
218	err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx);
219	if (err) {
220		log_err("Failed to allocate quota context", "");
221		return err;
222	}
223
224	memset(ctx, 0, sizeof(struct quota_ctx));
225	for (i = 0; i < MAXQUOTAS; i++) {
226		if ((qtype != -1) && (i != qtype))
227			continue;
228		err = ext2fs_get_mem(sizeof(dict_t), &dict);
229		if (err) {
230			log_err("Failed to allocate dictionary", "");
231			return err;
232		}
233		ctx->quota_dict[i] = dict;
234		dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp);
235		dict_set_allocator(dict, NULL, quota_dnode_free, NULL);
236	}
237
238	ctx->fs = fs;
239	*qctx = ctx;
240	return 0;
241}
242
243void quota_release_context(quota_ctx_t *qctx)
244{
245	dict_t	*dict;
246	int	i;
247	quota_ctx_t ctx;
248
249	if (!qctx)
250		return;
251
252	ctx = *qctx;
253	for (i = 0; i < MAXQUOTAS; i++) {
254		dict = ctx->quota_dict[i];
255		ctx->quota_dict[i] = 0;
256		if (dict) {
257			dict_free_nodes(dict);
258			free(dict);
259		}
260	}
261	*qctx = NULL;
262	free(ctx);
263}
264
265static struct dquot *get_dq(dict_t *dict, __u32 key)
266{
267	struct dquot	*dq;
268	dnode_t		*n;
269
270	n = dict_lookup(dict, UINT_TO_VOIDPTR(key));
271	if (n)
272		dq = dnode_get(n);
273	else {
274		if (ext2fs_get_mem(sizeof(struct dquot), &dq)) {
275			log_err("Unable to allocate dquot", "");
276			return NULL;
277		}
278		memset(dq, 0, sizeof(struct dquot));
279		dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq);
280		dq->dq_id = key;
281	}
282	return dq;
283}
284
285
286/*
287 * Called to update the blocks used by a particular inode
288 */
289void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
290		    qsize_t space)
291{
292	struct dquot	*dq;
293	dict_t		*dict;
294	int		i;
295
296	if (!qctx)
297		return;
298
299	log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
300			inode_uid(*inode),
301			inode_gid(*inode), space);
302	for (i = 0; i < MAXQUOTAS; i++) {
303		dict = qctx->quota_dict[i];
304		if (dict) {
305			dq = get_dq(dict, get_qid(inode, i));
306			if (dq)
307				dq->dq_dqb.dqb_curspace += space;
308		}
309	}
310}
311
312/*
313 * Called to remove some blocks used by a particular inode
314 */
315void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
316		    qsize_t space)
317{
318	struct dquot	*dq;
319	dict_t		*dict;
320	int		i;
321
322	if (!qctx)
323		return;
324
325	log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
326			inode_uid(*inode),
327			inode_gid(*inode), space);
328	for (i = 0; i < MAXQUOTAS; i++) {
329		dict = qctx->quota_dict[i];
330		if (dict) {
331			dq = get_dq(dict, get_qid(inode, i));
332			dq->dq_dqb.dqb_curspace -= space;
333		}
334	}
335}
336
337/*
338 * Called to count the files used by an inode's user/group
339 */
340void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode,
341		       ext2_ino_t ino, int adjust)
342{
343	struct dquot	*dq;
344	dict_t		*dict;
345	int		i;
346
347	if (!qctx)
348		return;
349
350	log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino,
351			inode_uid(*inode),
352			inode_gid(*inode), adjust);
353	for (i = 0; i < MAXQUOTAS; i++) {
354		dict = qctx->quota_dict[i];
355		if (dict) {
356			dq = get_dq(dict, get_qid(inode, i));
357			dq->dq_dqb.dqb_curinodes += adjust;
358		}
359	}
360}
361
362errcode_t quota_compute_usage(quota_ctx_t qctx)
363{
364	ext2_filsys fs;
365	ext2_ino_t ino;
366	errcode_t ret;
367	struct ext2_inode inode;
368	qsize_t space;
369	ext2_inode_scan scan;
370
371	if (!qctx)
372		return 0;
373
374	fs = qctx->fs;
375	ret = ext2fs_open_inode_scan(fs, 0, &scan);
376	if (ret) {
377		log_err("while opening inode scan. ret=%ld", ret);
378		return ret;
379	}
380
381	while (1) {
382		ret = ext2fs_get_next_inode(scan, &ino, &inode);
383		if (ret) {
384			log_err("while getting next inode. ret=%ld", ret);
385			ext2fs_close_inode_scan(scan);
386			return ret;
387		}
388		if (ino == 0)
389			break;
390		if (inode.i_links_count) {
391			space = ext2fs_inode_i_blocks(fs, &inode) << 9;
392			quota_data_add(qctx, &inode, ino, space);
393			quota_data_inodes(qctx, &inode, ino, +1);
394		}
395	}
396
397	ext2fs_close_inode_scan(scan);
398
399	return 0;
400}
401
402struct scan_dquots_data {
403	dict_t		*quota_dict;
404	int             update_limits; /* update limits from disk */
405	int		update_usage;
406	int		usage_is_inconsistent;
407};
408
409static int scan_dquots_callback(struct dquot *dquot, void *cb_data)
410{
411	struct scan_dquots_data *scan_data = cb_data;
412	dict_t *quota_dict = scan_data->quota_dict;
413	struct dquot *dq;
414
415	dq = get_dq(quota_dict, dquot->dq_id);
416	dq->dq_id = dquot->dq_id;
417
418	/* Check if there is inconsistancy. */
419	if (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace ||
420	    dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes) {
421		scan_data->usage_is_inconsistent = 1;
422		log_err("Usage inconsistent for ID %d: (%llu, %llu) != "
423			"(%llu,	%llu)", dq->dq_id, dq->dq_dqb.dqb_curspace,
424			dq->dq_dqb.dqb_curinodes, dquot->dq_dqb.dqb_curspace,
425			dquot->dq_dqb.dqb_curinodes);
426	}
427
428	if (scan_data->update_limits) {
429		dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
430		dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
431		dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
432		dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
433	}
434
435	if (scan_data->update_usage) {
436		dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace;
437		dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes;
438	}
439
440	return 0;
441}
442
443/*
444 * Read all dquots from quota file into memory
445 */
446static errcode_t quota_read_all_dquots(struct quota_handle *qh,
447                                       quota_ctx_t qctx, int update_limits)
448{
449	struct scan_dquots_data scan_data;
450
451	scan_data.quota_dict = qctx->quota_dict[qh->qh_type];
452	scan_data.update_limits = update_limits;
453	scan_data.update_usage = 0;
454
455	return qh->qh_ops->scan_dquots(qh, scan_dquots_callback, &scan_data);
456}
457
458/*
459 * Write all memory dquots into quota file
460 */
461static errcode_t quota_write_all_dquots(struct quota_handle *qh,
462                                        quota_ctx_t qctx)
463{
464	errcode_t err;
465
466	err = ext2fs_read_bitmaps(qctx->fs);
467	if (err)
468		return err;
469	write_dquots(qctx->quota_dict[qh->qh_type], qh);
470	ext2fs_mark_bb_dirty(qctx->fs);
471	qctx->fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
472	ext2fs_write_bitmaps(qctx->fs);
473	return 0;
474}
475
476/*
477 * Update usage of in quota file, limits keep unchaged
478 */
479errcode_t quota_update_inode(quota_ctx_t qctx, ext2_ino_t qf_ino, int type)
480{
481	struct quota_handle *qh;
482	errcode_t err;
483
484	if (!qctx)
485		return 0;
486
487	err = ext2fs_get_mem(sizeof(struct quota_handle), &qh);
488	if (err) {
489		log_err("Unable to allocate quota handle", "");
490		return err;
491	}
492
493	err = quota_file_open(qh, qctx->fs, qf_ino, type, -1, EXT2_FILE_WRITE);
494	if (err) {
495		log_err("Open quota file failed", "");
496		goto out;
497	}
498
499	quota_read_all_dquots(qh, qctx, 1);
500	quota_write_all_dquots(qh, qctx);
501
502	err = quota_file_close(qh);
503	if (err) {
504		log_err("Cannot finish IO on new quotafile: %s",
505			strerror(errno));
506		if (qh->qh_qf.e2_file)
507			ext2fs_file_close(qh->qh_qf.e2_file);
508	}
509out:
510	ext2fs_free_mem(&qh);
511	return err;
512}
513
514/*
515 * Compares the measured quota in qctx->quota_dict with that in the quota inode
516 * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
517 * set to 1 if the supplied and on-disk quota usage values are not identical.
518 */
519errcode_t quota_compare_and_update(quota_ctx_t qctx, int qtype,
520				   int *usage_inconsistent)
521{
522	ext2_filsys fs = qctx->fs;
523	struct quota_handle qh;
524	struct scan_dquots_data scan_data;
525	ext2_ino_t qf_ino;
526	errcode_t err = 0;
527
528	if (!qctx->quota_dict[qtype])
529		goto out;
530
531	qf_ino = qtype == USRQUOTA ? fs->super->s_usr_quota_inum :
532				     fs->super->s_grp_quota_inum;
533	err = quota_file_open(&qh, fs, qf_ino, qtype, -1, 0);
534	if (err) {
535		log_err("Open quota file failed", "");
536		goto out;
537	}
538
539	scan_data.quota_dict = qctx->quota_dict[qtype];
540	scan_data.update_limits = 1;
541	scan_data.update_usage = 0;
542	scan_data.usage_is_inconsistent = 0;
543	err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
544	if (err) {
545		log_err("Error scanning dquots", "");
546		goto out;
547	}
548	*usage_inconsistent = scan_data.usage_is_inconsistent;
549
550out:
551	return err;
552}
553