mkquota.c revision f239fefc14226f655477179801c734749a04d4b4
1/*
2 * mkquota.c --- create quota files for a filesystem
3 *
4 * Aditya Kali <adityakali@google.com>
5 */
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <unistd.h>
9#include <errno.h>
10#include <string.h>
11#include <fcntl.h>
12
13#include "ext2fs/ext2_fs.h"
14#include "ext2fs/ext2fs.h"
15#include "e2p/e2p.h"
16
17#include "quota.h"
18#include "quotaio.h"
19#include "quotaio_v2.h"
20#include "quotaio_tree.h"
21#include "mkquota.h"
22#include "common.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
28static void print_inode(struct ext2_inode *inode)
29{
30	if (!inode)
31		return;
32
33	fprintf(stderr, "  i_mode = %d\n", inode->i_mode);
34	fprintf(stderr, "  i_uid = %d\n", inode->i_uid);
35	fprintf(stderr, "  i_size = %d\n", inode->i_size);
36	fprintf(stderr, "  i_atime = %d\n", inode->i_atime);
37	fprintf(stderr, "  i_ctime = %d\n", inode->i_ctime);
38	fprintf(stderr, "  i_mtime = %d\n", inode->i_mtime);
39	fprintf(stderr, "  i_dtime = %d\n", inode->i_dtime);
40	fprintf(stderr, "  i_gid = %d\n", inode->i_gid);
41	fprintf(stderr, "  i_links_count = %d\n", inode->i_links_count);
42	fprintf(stderr, "  i_blocks = %d\n", inode->i_blocks);
43	fprintf(stderr, "  i_flags = %d\n", inode->i_flags);
44
45	return;
46}
47
48int is_quota_on(ext2_filsys fs, int type)
49{
50	char tmp[1024];
51	qid_t id = (type == USRQUOTA) ? getuid() : getgid();
52
53	if (!quotactl(QCMD(Q_V2_GETQUOTA, type), fs->device_name, id, tmp))
54		return 1;
55	return 0;
56}
57
58/*
59 * Returns 0 if not able to find the quota file, otherwise returns its
60 * inode number.
61 */
62int quota_file_exists(ext2_filsys fs, int qtype, int fmt)
63{
64	char qf_name[256];
65	errcode_t ret;
66	ext2_ino_t ino;
67
68	if (qtype >= MAXQUOTAS || fmt > QFMT_VFS_V1)
69		return -EINVAL;
70
71	get_qf_name(qtype, fmt, qf_name);
72
73	ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0,
74			    &ino);
75	if (ret)
76		return 0;
77
78	return ino;
79}
80
81/*
82 * Set the value for reserved quota inode number field in superblock.
83 */
84void set_sb_quota_inum(ext2_filsys fs, ext2_ino_t ino, int qtype)
85{
86	ext2_ino_t *inump;
87
88	inump = (qtype == USRQUOTA) ? &fs->super->s_usr_quota_inum :
89		&fs->super->s_grp_quota_inum;
90
91	log_debug("setting quota ino in superblock: ino=%u, type=%d", ino,
92		 qtype);
93	*inump = ino;
94	ext2fs_mark_super_dirty(fs);
95}
96
97errcode_t remove_quota_inode(ext2_filsys fs, int qtype)
98{
99	ext2_ino_t qf_ino;
100
101	ext2fs_read_bitmaps(fs);
102	qf_ino = (qtype == USRQUOTA) ? fs->super->s_usr_quota_inum :
103		fs->super->s_grp_quota_inum;
104	set_sb_quota_inum(fs, 0, qtype);
105	/* Truncate the inode only if its a reserved one. */
106	if (qf_ino < EXT2_FIRST_INODE(fs->super))
107		truncate_quota_inode(fs, qf_ino);
108
109	ext2fs_mark_super_dirty(fs);
110	ext2fs_write_bitmaps(fs);
111	return 0;
112}
113
114static void write_dquots(dict_t *dict, struct quota_handle *qh)
115{
116	int		i = 0;
117	dnode_t		*n;
118	struct dquot	*dq;
119	__u32		key;
120
121	for (n = dict_first(dict); n; n = dict_next(dict, n)) {
122		dq = dnode_get(n);
123		if (dq) {
124			dq->dq_h = qh;
125			update_grace_times(dq);
126			qh->qh_ops->commit_dquot(dq, COMMIT_ALL);
127		}
128	}
129}
130
131errcode_t write_quota_inode(quota_ctx_t qctx, int qtype)
132{
133	int		retval, i;
134	unsigned long	qf_inums[MAXQUOTAS];
135	struct dquot	*dquot;
136	dict_t		*dict;
137	ext2_filsys	fs;
138	struct quota_handle *h;
139	int		fmt = QFMT_VFS_V1;
140
141	if (!qctx)
142		return;
143
144	fs = qctx->fs;
145	h = smalloc(sizeof(struct quota_handle));
146	ext2fs_read_bitmaps(fs);
147
148	for (i = 0; i < MAXQUOTAS; i++) {
149		if ((qtype != -1) && (i != qtype))
150			continue;
151
152		dict = qctx->quota_dict[i];
153		if (!dict)
154			continue;
155
156		retval = new_io(h, fs, i, fmt);
157		if (retval < 0) {
158			log_err("Cannot initialize io on quotafile", "");
159			continue;
160		}
161
162		write_dquots(dict, h);
163		retval = end_io(h);
164		if (retval < 0) {
165			log_err("Cannot finish IO on new quotafile: %s",
166				strerror(errno));
167			if (h->qh_qf.e2_file)
168				ext2fs_file_close(h->qh_qf.e2_file);
169			truncate_quota_inode(fs, h->qh_qf.ino);
170			continue;
171		}
172
173		/* Set quota inode numbers in superblock. */
174		set_sb_quota_inum(fs, h->qh_qf.ino, i);
175		ext2fs_mark_super_dirty(fs);
176		ext2fs_mark_bb_dirty(fs);
177		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
178	}
179
180	ext2fs_write_bitmaps(fs);
181out:
182	free(h);
183	return retval;
184}
185
186/******************************************************************/
187/* Helper functions for computing quota in memory.                */
188/******************************************************************/
189
190static int dict_uint_cmp(const void *a, const void *b)
191{
192	unsigned int	c, d;
193
194	c = VOIDPTR_TO_UINT(a);
195	d = VOIDPTR_TO_UINT(b);
196
197	return c - d;
198}
199
200static qid_t get_qid(struct ext2_inode *inode, int qtype)
201{
202	switch (qtype) {
203	case USRQUOTA:
204		return inode_uid(*inode);
205	case GRPQUOTA:
206		return inode_gid(*inode);
207	default:
208		log_err("Invalid quota type: %d", qtype);
209		BUG_ON(1);
210	}
211}
212
213static void quota_dnode_free(dnode_t *node,
214			     void *context EXT2FS_ATTR((unused)))
215{
216	void *ptr = node ? dnode_get(node) : 0;
217
218	free(ptr);
219	free(node);
220}
221
222/*
223 * Called in Pass #1 to set up the quota tracking data structures.
224 */
225void init_quota_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype)
226{
227	int	i;
228	dict_t	*dict;
229	quota_ctx_t ctx;
230
231	ctx = (quota_ctx_t)smalloc(sizeof(struct quota_ctx));
232	memset(ctx, 0, sizeof(struct quota_ctx));
233	for (i = 0; i < MAXQUOTAS; i++) {
234		if ((qtype != -1) && (i != qtype))
235			continue;
236		dict = (dict_t *)smalloc(sizeof(dict_t));
237		ctx->quota_dict[i] = dict;
238		dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp);
239		dict_set_allocator(dict, NULL, quota_dnode_free, NULL);
240	}
241
242	ctx->fs = fs;
243	*qctx = ctx;
244}
245
246void release_quota_context(quota_ctx_t *qctx)
247{
248	dict_t	*dict;
249	int	i;
250	quota_ctx_t ctx;
251
252	if (!qctx)
253		return;
254
255	ctx = *qctx;
256	for (i = 0; i < MAXQUOTAS; i++) {
257		dict = ctx->quota_dict[i];
258		ctx->quota_dict[i] = 0;
259		if (dict) {
260			dict_free_nodes(dict);
261			free(dict);
262		}
263	}
264	*qctx = NULL;
265	free(ctx);
266}
267
268static struct dquot *get_dq(dict_t *dict, __u32 key)
269{
270	struct dquot	*dq;
271	dnode_t		*n;
272
273	n = dict_lookup(dict, UINT_TO_VOIDPTR(key));
274	if (n)
275		dq = dnode_get(n);
276	else {
277		dq = smalloc(sizeof(struct dquot));
278		memset(dq, 0, sizeof(struct dquot));
279		dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq);
280	}
281	return dq;
282}
283
284
285/*
286 * Called to update the blocks used by a particular inode
287 */
288void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
289		    qsize_t space)
290{
291	struct dquot	*dq;
292	dict_t		*dict;
293	int		i;
294
295	if (!qctx)
296		return;
297
298	log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
299			inode_uid(*inode),
300			inode_gid(*inode), space);
301	for (i = 0; i < MAXQUOTAS; i++) {
302		dict = qctx->quota_dict[i];
303		if (dict) {
304			dq = get_dq(dict, get_qid(inode, i));
305			dq->dq_dqb.dqb_curspace += space;
306		}
307	}
308}
309
310/*
311 * Called to remove some blocks used by a particular inode
312 */
313void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
314		    qsize_t space)
315{
316	struct dquot	*dq;
317	dict_t		*dict;
318	int		i;
319
320	if (!qctx)
321		return;
322
323	log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
324			inode_uid(*inode),
325			inode_gid(*inode), space);
326	for (i = 0; i < MAXQUOTAS; i++) {
327		dict = qctx->quota_dict[i];
328		if (dict) {
329			dq = get_dq(dict, get_qid(inode, i));
330			dq->dq_dqb.dqb_curspace -= space;
331		}
332	}
333}
334
335/*
336 * Called to count the files used by an inode's user/group
337 */
338void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode,
339		       ext2_ino_t ino, int adjust)
340{
341	struct dquot	*dq;
342	dict_t		*dict;
343	int		i;
344
345	if (!qctx)
346		return;
347
348	log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino,
349			inode_uid(*inode),
350			inode_gid(*inode), adjust);
351	for (i = 0; i < MAXQUOTAS; i++) {
352		dict = qctx->quota_dict[i];
353		if (dict) {
354			dq = get_dq(dict, get_qid(inode, i));
355			dq->dq_dqb.dqb_curinodes += adjust;
356		}
357	}
358}
359
360errcode_t compute_quota(quota_ctx_t qctx, int qtype)
361{
362	ext2_filsys fs;
363	const char *name = "lost+found";
364	ext2_ino_t ino;
365	errcode_t ret;
366	struct ext2_inode inode;
367	qsize_t space;
368	ext2_inode_scan scan;
369
370	if (!qctx)
371		return;
372
373	fs = qctx->fs;
374	ret = ext2fs_open_inode_scan(fs, 0, &scan);
375	if (ret) {
376		log_err("while opening inode scan. ret=%ld", ret);
377		return ret;
378	}
379
380	while (1) {
381		ret = ext2fs_get_next_inode(scan, &ino, &inode);
382		if (ret) {
383			log_err("while getting next inode. ret=%ld", ret);
384			ext2fs_close_inode_scan(scan);
385			return ret;
386		}
387		if (ino == 0)
388			break;
389		if (inode.i_links_count) {
390			/* Convert i_blocks to # of 1k blocks */
391			space = (ext2fs_inode_i_blocks(fs, &inode) + 1) >> 1;
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