]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/quot.c
Initial version of xfs quota utility. Knows how to do user/group/project quota,...
[thirdparty/xfsprogs-dev.git] / quota / quot.c
1 /*
2 * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc., 59
21 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
31 */
32
33 #include <xfs/command.h>
34 #include <ctype.h>
35 #include <pwd.h>
36 #include <grp.h>
37 #include "init.h"
38 #include "quota.h"
39
40 typedef struct du {
41 struct du *next;
42 __uint64_t blocks;
43 __uint64_t blocks30;
44 __uint64_t blocks60;
45 __uint64_t blocks90;
46 __uint64_t nfiles;
47 __uint32_t id;
48 } du_t;
49
50 #define TSIZE 500
51 static __uint64_t sizes[TSIZE];
52 static __uint64_t overflow;
53
54 #define NDU 60000
55 #define DUHASH 8209
56 static du_t du[3][NDU];
57 static du_t *duhash[3][DUHASH];
58 static int ndu[3]; /* #usr/grp/prj */
59
60 #define NBSTAT 4069
61
62 static time_t now;
63 static cmdinfo_t quot_cmd;
64
65 static void
66 quot_help(void)
67 {
68 printf(_(
69 "\n"
70 " display a summary of filesystem ownership\n"
71 "\n"
72 " -a -- summarise for all local XFS filesystem mount points\n"
73 " -c -- display three columns giving file size in kilobytes, number of files\n"
74 " of that size, and cumulative total of kilobytes in that size or\n"
75 " smaller file. The last row is used as an overflow bucket and is the\n"
76 " total of all files greater than 500 kilobytes.\n"
77 " -v -- display three columns containing the number of kilobytes not\n"
78 " accessed in the last 30, 60, and 90 days.\n"
79 " -g -- display group summary\n"
80 " -p -- display project summary\n"
81 " -u -- display user summary\n"
82 " -b -- display number of blocks used\n"
83 " -i -- display number of inodes used\n"
84 " -r -- display number of realtime blocks used\n"
85 " -n -- suppress the initial header\n"
86 " -f -- send output to a file\n"
87 " The (optional) user/group/project can be specified either by name or by\n"
88 " number (i.e. uid/gid/projid).\n"
89 "\n"));
90 }
91
92 static void
93 quot_bulkstat_add(
94 xfs_bstat_t *p,
95 uint flags)
96 {
97 du_t *dp;
98 du_t **hp;
99 __uint64_t size;
100 __uint32_t i, id;
101
102 if ((p->bs_mode & S_IFMT) == 0)
103 return;
104 size = howmany((p->bs_blocks * p->bs_blksize), 0x400ULL);
105
106 if (flags & HISTOGRAM_FLAG) {
107 if (!(S_ISDIR(p->bs_mode) || S_ISREG(p->bs_mode)))
108 return;
109 if (size >= TSIZE) {
110 overflow += size;
111 size = TSIZE - 1;
112 }
113 sizes[(int)size]++;
114 return;
115 }
116 for (i = 0; i < 3; i++) {
117 id = (i == 0) ? p->bs_uid : ((i == 1) ?
118 p->bs_gid : p->bs_projid);
119 hp = &duhash[i][id % DUHASH];
120 for (dp = *hp; dp; dp = dp->next)
121 if (dp->id == id)
122 break;
123 if (dp == 0) {
124 if (ndu[i] >= NDU)
125 return;
126 dp = &du[i][(ndu[i]++)];
127 dp->next = *hp;
128 *hp = dp;
129 dp->id = id;
130 dp->nfiles = 0;
131 dp->blocks = 0;
132 dp->blocks30 = 0;
133 dp->blocks60 = 0;
134 dp->blocks90 = 0;
135 }
136 dp->blocks += size;
137
138 if (now - p->bs_atime.tv_sec > 30 * (60*60*24))
139 dp->blocks30 += size;
140 if (now - p->bs_atime.tv_sec > 60 * (60*60*24))
141 dp->blocks60 += size;
142 if (now - p->bs_atime.tv_sec > 90 * (60*60*24))
143 dp->blocks90 += size;
144 dp->nfiles++;
145 }
146 }
147
148 static void
149 quot_bulkstat_mount(
150 char *fsdir,
151 uint flags)
152 {
153 xfs_fsop_bulkreq_t bulkreq;
154 xfs_bstat_t *buf;
155 __int64_t last = 0;
156 __int32_t count;
157 int i, sts, fsfd;
158 du_t **dp;
159
160 /*
161 * Initialize tables between checks; because of the qsort
162 * in report() the hash tables must be rebuilt each time.
163 */
164 for (sts = 0; sts < TSIZE; sts++)
165 sizes[sts] = 0;
166 overflow = 0;
167 for (i = 0; i < 3; i++)
168 for (dp = duhash[i]; dp < &duhash[i][DUHASH]; dp++)
169 *dp = 0;
170 ndu[0] = ndu[1] = ndu[2] = 0;
171
172 fsfd = open(fsdir, O_RDONLY);
173 if (fsfd < 0) {
174 perror(fsdir);
175 return;
176 }
177
178 buf = (xfs_bstat_t *)calloc(NBSTAT, sizeof(xfs_bstat_t));
179 if (!buf) {
180 perror("calloc");
181 return;
182 }
183
184 bulkreq.lastip = &last;
185 bulkreq.icount = NBSTAT;
186 bulkreq.ubuffer = buf;
187 bulkreq.ocount = &count;
188
189 while ((sts = xfsctl(fsdir, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq)) == 0) {
190 if (count == 0)
191 break;
192 for (i = 0; i < count; i++)
193 quot_bulkstat_add(&buf[i], flags);
194 }
195 if (sts < 0)
196 perror("XFS_IOC_FSBULKSTAT"),
197 free(buf);
198 close(fsfd);
199 }
200
201 static int
202 qcompare(
203 du_t *p1,
204 du_t *p2)
205 {
206 if (p1->blocks > p2->blocks)
207 return -1;
208 if (p1->blocks < p2->blocks)
209 return 1;
210 if (p1->id > p2->id)
211 return 1;
212 else if (p1->id < p2->id)
213 return -1;
214 return 0;
215 }
216
217 typedef char *(*idtoname_t)(__uint32_t);
218
219 static void
220 quot_report_mount_any_type(
221 FILE *fp,
222 du_t *dp,
223 int count,
224 idtoname_t names,
225 uint form,
226 uint type,
227 fs_path_t *mount,
228 uint flags)
229 {
230 char *cp;
231
232 fprintf(fp, _("%s (%s) %s:\n"),
233 mount->fs_name, mount->fs_dir, type_to_string(type));
234 qsort(dp, count, sizeof(dp[0]),
235 (int (*)(const void *, const void *))qcompare);
236 for (; dp < &dp[count]; dp++) {
237 if (dp->blocks == 0)
238 return;
239 fprintf(fp, "%8llu ", (unsigned long long) dp->blocks);
240 if (form & XFS_INODE_QUOTA)
241 fprintf(fp, "%8llu ",
242 (unsigned long long) dp->nfiles);
243 if ((cp = (names)(dp->id)) != NULL)
244 fprintf(fp, "%-8.8s", cp);
245 else
246 fprintf(fp, "#%-7d", dp->id);
247 if (flags & VERBOSE_FLAG)
248 fprintf(fp, " %8llu %8llu %8llu",
249 (unsigned long long) dp->blocks30,
250 (unsigned long long) dp->blocks60,
251 (unsigned long long) dp->blocks90);
252 fputc('\n', fp);
253 }
254 }
255
256 static void
257 quot_report_mount(
258 FILE *fp,
259 uint form,
260 uint type,
261 fs_path_t *mount,
262 uint flags)
263 {
264 switch (type) {
265 case XFS_GROUP_QUOTA:
266 quot_report_mount_any_type(fp, du[1], ndu[1], gid_to_name,
267 form, type, mount, flags);
268 break;
269 case XFS_PROJ_QUOTA:
270 quot_report_mount_any_type(fp, du[2], ndu[2], prid_to_name,
271 form, type, mount, flags);
272 break;
273 case XFS_USER_QUOTA:
274 quot_report_mount_any_type(fp, du[0], ndu[0], uid_to_name,
275 form, type, mount, flags);
276 }
277 }
278
279 static void
280 quot_report(
281 FILE *fp,
282 uint form,
283 uint type,
284 char *dir,
285 uint flags)
286 {
287 fs_cursor_t cursor;
288 fs_path_t *mount;
289
290 now = time(0);
291 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
292 while ((mount = fs_cursor_next_entry(&cursor))) {
293 quot_bulkstat_mount(mount->fs_dir, flags);
294 quot_report_mount(fp, form, type, mount, flags);
295 }
296 }
297
298 static void
299 quot_histogram_mount(
300 FILE *fp,
301 fs_path_t *mount,
302 uint flags)
303 {
304 __uint64_t t = 0;
305 int i;
306
307 fprintf(fp, _("%s (%s):\n"), mount->fs_name, mount->fs_dir);
308
309 for (i = 0; i < TSIZE - 1; i++)
310 if (sizes[i] > 0) {
311 t += sizes[i] * i;
312 fprintf(fp, _("%d\t%llu\t%llu\n"), i,
313 (unsigned long long) sizes[i],
314 (unsigned long long) t);
315 }
316 fprintf(fp, _("%d\t%llu\t%llu\n"), TSIZE - 1,
317 (unsigned long long) sizes[TSIZE - 1],
318 (unsigned long long) (overflow + t));
319 }
320
321 static void
322 quot_histogram(
323 FILE *fp,
324 char *dir,
325 uint flags)
326 {
327 fs_cursor_t cursor;
328 fs_path_t *mount;
329
330 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
331 while ((mount = fs_cursor_next_entry(&cursor))) {
332 quot_bulkstat_mount(mount->fs_dir, flags);
333 quot_histogram_mount(fp, mount, flags);
334 }
335 }
336
337 static void
338 quot_any_type(
339 FILE *fp,
340 uint form,
341 uint type,
342 char *dir,
343 uint flags)
344 {
345 if (flags & HISTOGRAM_FLAG)
346 quot_histogram(fp, dir, flags);
347 else
348 quot_report(fp, form, type, dir, flags);
349 }
350
351 static int
352 quot_f(
353 int argc,
354 char **argv)
355 {
356 FILE *fp = NULL;
357 char *fname = NULL;
358 int c, flags = 0, type = 0, form = 0;
359
360 while ((c = getopt(argc, argv, "abcf:hgipruv")) != EOF) {
361 switch (c) {
362 case 'f':
363 fname = optarg;
364 break;
365 case 'b':
366 form |= XFS_BLOCK_QUOTA;
367 break;
368 case 'i':
369 form |= XFS_INODE_QUOTA;
370 break;
371 case 'r':
372 form |= XFS_RTBLOCK_QUOTA;
373 break;
374 case 'g':
375 type = XFS_GROUP_QUOTA;
376 break;
377 case 'p':
378 type = XFS_PROJ_QUOTA;
379 break;
380 case 'u':
381 type = XFS_USER_QUOTA;
382 break;
383 case 'a':
384 flags |= ALL_MOUNTS_FLAG;
385 break;
386 case 'c':
387 flags |= HISTOGRAM_FLAG;
388 break;
389 case 'v':
390 flags |= VERBOSE_FLAG;
391 break;
392 default:
393 return command_usage(&quot_cmd);
394 }
395 }
396
397 if (!form)
398 form = XFS_BLOCK_QUOTA;
399
400 if (!type)
401 type = XFS_USER_QUOTA;
402
403 if ((fp = fopen_write_secure(fname)) == NULL)
404 return 0;
405
406 if (argc == optind)
407 quot_any_type(fp, form, type, (flags & ALL_MOUNTS_FLAG) ?
408 NULL : fs_path->fs_dir, flags);
409 else while (argc > optind)
410 quot_any_type(fp, form, type, argv[optind++], flags);
411
412 if (fname)
413 fclose(fp);
414 return 0;
415 }
416
417 void
418 quot_init(void)
419 {
420 quot_cmd.name = _("quot");
421 quot_cmd.cfunc = quot_f;
422 quot_cmd.argmin = 0;
423 quot_cmd.argmax = -1;
424 quot_cmd.args = _("[-bir] [-gpu] [-acv] [-f file]");
425 quot_cmd.oneline = _("summarize filesystem ownership");
426 quot_cmd.help = quot_help;
427
428 if (expert)
429 add_command(&quot_cmd);
430 }