1/*
2 * quota.c --- debugfs quota commands
3 *
4 * Copyright (C) 2014 Theodore Ts'o.  This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
8#include "config.h"
9#include <stdio.h>
10#include <unistd.h>
11#include <stdlib.h>
12#include <ctype.h>
13#include <string.h>
14#include <time.h>
15#ifdef HAVE_ERRNO_H
16#include <errno.h>
17#endif
18#include <sys/types.h>
19#ifdef HAVE_GETOPT_H
20#include <getopt.h>
21#else
22extern int optind;
23extern char *optarg;
24#endif
25
26#include "debugfs.h"
27
28const char *quota_type[] = { "user", "group", NULL };
29
30static int load_quota_ctx(char *progname)
31{
32	errcode_t	retval;
33
34	if (check_fs_open(progname))
35		return 1;
36
37	if (!ext2fs_has_feature_quota(current_fs->super)) {
38		com_err(progname, 0, "quota feature not enabled");
39		return 1;
40	}
41
42	if (current_qctx)
43		return 0;
44
45	retval = quota_init_context(&current_qctx, current_fs, QUOTA_ALL_BIT);
46	if (retval) {
47		com_err(current_fs->device_name, retval,
48			"while trying to load quota information");
49		return 1;
50	}
51	return 0;
52}
53
54static int parse_quota_type(const char *cmdname, const char *str)
55{
56	errcode_t	retval;
57	char		*t;
58	int		flags = 0;
59	int		i;
60
61	for (i = 0; i < MAXQUOTAS; i++) {
62		if (strcasecmp(str, quota_type[i]) == 0)
63			break;
64	}
65	if (i >= MAXQUOTAS) {
66		i = strtol(str, &t, 0);
67		if (*t)
68			i = -1;
69	}
70	if (i < 0 || i >= MAXQUOTAS) {
71		com_err(0, 0, "Invalid quota type: %s", str);
72		printf("Valid quota types are: ");
73		for (i = 0; i < MAXQUOTAS; i++)
74			printf("%s ", quota_type[i]);
75		printf("\n");
76		return -1;
77	}
78
79	if (current_fs->flags & EXT2_FLAG_RW)
80		flags |= EXT2_FILE_WRITE;
81
82	retval = quota_file_open(current_qctx, NULL, 0, i, -1, flags);
83	if (retval) {
84		com_err(cmdname, retval,
85			"while opening quota inode (type %d)", i);
86		return -1;
87	}
88	return i;
89}
90
91
92static int list_quota_callback(struct dquot *dq,
93			       void *cb_data EXT2FS_ATTR((unused)))
94{
95	printf("%8u   %8lld %8lld %8lld    %8lld %8lld %8lld\n",
96	       dq->dq_id, (long long)dq->dq_dqb.dqb_curspace,
97	       (long long)dq->dq_dqb.dqb_bsoftlimit,
98	       (long long)dq->dq_dqb.dqb_bhardlimit,
99	       (long long)dq->dq_dqb.dqb_curinodes,
100	       (long long)dq->dq_dqb.dqb_isoftlimit,
101	       (long long)dq->dq_dqb.dqb_ihardlimit);
102	return 0;
103}
104
105void do_list_quota(int argc, char *argv[])
106{
107	errcode_t	retval;
108	int		type;
109	struct quota_handle *qh;
110
111	if (load_quota_ctx(argv[0]))
112		return;
113
114	if (argc != 2) {
115		com_err(0, 0, "Usage: list_quota <quota_type>\n");
116		return;
117	}
118
119	type = parse_quota_type(argv[0], argv[1]);
120	if (type < 0)
121		return;
122
123	printf("%8s   %8s %8s %8s    %8s %8s %8s\n",
124	       (type == 0) ? "user id" : "group id",
125	       "blocks", "quota", "limit", "inodes", "quota", "limit");
126	qh = current_qctx->quota_file[type];
127	retval = qh->qh_ops->scan_dquots(qh, list_quota_callback, NULL);
128	if (retval) {
129		com_err(argv[0], retval, "while scanning dquots");
130		return;
131	}
132}
133
134void do_get_quota(int argc, char *argv[])
135{
136	int		err, type;
137	struct quota_handle *qh;
138	struct dquot	*dq;
139	qid_t		id;
140
141	if (load_quota_ctx(argv[0]))
142		return;
143
144	if (argc != 3) {
145		com_err(0, 0, "Usage: get_quota <quota_type> <id>\n");
146		return;
147	}
148
149	type = parse_quota_type(argv[0], argv[1]);
150	if (type < 0)
151		return;
152
153	id = parse_ulong(argv[2], argv[0], "id", &err);
154	if (err)
155		return;
156
157	printf("%8s   %8s %8s %8s    %8s %8s %8s\n",
158	       (type == 0) ? "user id" : "group id",
159	       "blocks", "quota", "limit", "inodes", "quota", "limit");
160
161	qh = current_qctx->quota_file[type];
162
163	dq = qh->qh_ops->read_dquot(qh, id);
164	if (dq) {
165		list_quota_callback(dq, NULL);
166		ext2fs_free_mem(&dq);
167	} else {
168		com_err(argv[0], 0, "couldn't read quota record");
169	}
170}
171