]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/quot.c
Allow swab.h to be used in -pedantic c++ build environments.
[thirdparty/xfsprogs-dev.git] / quota / quot.c
CommitLineData
5aead01d
NS
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
40typedef 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
51static __uint64_t sizes[TSIZE];
52static __uint64_t overflow;
53
54#define NDU 60000
55#define DUHASH 8209
56static du_t du[3][NDU];
57static du_t *duhash[3][DUHASH];
58static int ndu[3]; /* #usr/grp/prj */
59
60#define NBSTAT 4069
61
62static time_t now;
63static cmdinfo_t quot_cmd;
64
65static void
66quot_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"
1774874a
NS
85" -n -- skip identifier-to-name translations, just report IDs\n"
86" -N -- suppress the initial header\n"
5aead01d
NS
87" -f -- send output to a file\n"
88" The (optional) user/group/project can be specified either by name or by\n"
89" number (i.e. uid/gid/projid).\n"
90"\n"));
91}
92
93static void
94quot_bulkstat_add(
95 xfs_bstat_t *p,
96 uint flags)
97{
98 du_t *dp;
99 du_t **hp;
100 __uint64_t size;
101 __uint32_t i, id;
102
103 if ((p->bs_mode & S_IFMT) == 0)
104 return;
105 size = howmany((p->bs_blocks * p->bs_blksize), 0x400ULL);
106
107 if (flags & HISTOGRAM_FLAG) {
108 if (!(S_ISDIR(p->bs_mode) || S_ISREG(p->bs_mode)))
109 return;
110 if (size >= TSIZE) {
111 overflow += size;
112 size = TSIZE - 1;
113 }
114 sizes[(int)size]++;
115 return;
116 }
117 for (i = 0; i < 3; i++) {
118 id = (i == 0) ? p->bs_uid : ((i == 1) ?
119 p->bs_gid : p->bs_projid);
120 hp = &duhash[i][id % DUHASH];
121 for (dp = *hp; dp; dp = dp->next)
122 if (dp->id == id)
123 break;
124 if (dp == 0) {
125 if (ndu[i] >= NDU)
126 return;
127 dp = &du[i][(ndu[i]++)];
128 dp->next = *hp;
129 *hp = dp;
130 dp->id = id;
131 dp->nfiles = 0;
132 dp->blocks = 0;
133 dp->blocks30 = 0;
134 dp->blocks60 = 0;
135 dp->blocks90 = 0;
136 }
137 dp->blocks += size;
138
139 if (now - p->bs_atime.tv_sec > 30 * (60*60*24))
140 dp->blocks30 += size;
141 if (now - p->bs_atime.tv_sec > 60 * (60*60*24))
142 dp->blocks60 += size;
143 if (now - p->bs_atime.tv_sec > 90 * (60*60*24))
144 dp->blocks90 += size;
145 dp->nfiles++;
146 }
147}
148
149static void
150quot_bulkstat_mount(
151 char *fsdir,
152 uint flags)
153{
154 xfs_fsop_bulkreq_t bulkreq;
155 xfs_bstat_t *buf;
156 __int64_t last = 0;
157 __int32_t count;
158 int i, sts, fsfd;
159 du_t **dp;
160
161 /*
162 * Initialize tables between checks; because of the qsort
163 * in report() the hash tables must be rebuilt each time.
164 */
165 for (sts = 0; sts < TSIZE; sts++)
166 sizes[sts] = 0;
167 overflow = 0;
168 for (i = 0; i < 3; i++)
169 for (dp = duhash[i]; dp < &duhash[i][DUHASH]; dp++)
170 *dp = 0;
171 ndu[0] = ndu[1] = ndu[2] = 0;
172
173 fsfd = open(fsdir, O_RDONLY);
174 if (fsfd < 0) {
175 perror(fsdir);
176 return;
177 }
178
179 buf = (xfs_bstat_t *)calloc(NBSTAT, sizeof(xfs_bstat_t));
180 if (!buf) {
181 perror("calloc");
182 return;
183 }
184
185 bulkreq.lastip = &last;
186 bulkreq.icount = NBSTAT;
187 bulkreq.ubuffer = buf;
188 bulkreq.ocount = &count;
189
190 while ((sts = xfsctl(fsdir, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq)) == 0) {
191 if (count == 0)
192 break;
193 for (i = 0; i < count; i++)
194 quot_bulkstat_add(&buf[i], flags);
195 }
196 if (sts < 0)
197 perror("XFS_IOC_FSBULKSTAT"),
198 free(buf);
199 close(fsfd);
200}
201
202static int
203qcompare(
204 du_t *p1,
205 du_t *p2)
206{
207 if (p1->blocks > p2->blocks)
208 return -1;
209 if (p1->blocks < p2->blocks)
210 return 1;
211 if (p1->id > p2->id)
212 return 1;
213 else if (p1->id < p2->id)
214 return -1;
215 return 0;
216}
217
218typedef char *(*idtoname_t)(__uint32_t);
219
220static void
221quot_report_mount_any_type(
222 FILE *fp,
223 du_t *dp,
224 int count,
225 idtoname_t names,
226 uint form,
227 uint type,
228 fs_path_t *mount,
229 uint flags)
230{
231 char *cp;
232
233 fprintf(fp, _("%s (%s) %s:\n"),
234 mount->fs_name, mount->fs_dir, type_to_string(type));
235 qsort(dp, count, sizeof(dp[0]),
236 (int (*)(const void *, const void *))qcompare);
237 for (; dp < &dp[count]; dp++) {
238 if (dp->blocks == 0)
239 return;
240 fprintf(fp, "%8llu ", (unsigned long long) dp->blocks);
241 if (form & XFS_INODE_QUOTA)
242 fprintf(fp, "%8llu ",
243 (unsigned long long) dp->nfiles);
1774874a
NS
244 if (!(flags & NO_LOOKUP_FLAG) &&
245 ((cp = (names)(dp->id)) != NULL))
5aead01d
NS
246 fprintf(fp, "%-8.8s", cp);
247 else
248 fprintf(fp, "#%-7d", dp->id);
249 if (flags & VERBOSE_FLAG)
250 fprintf(fp, " %8llu %8llu %8llu",
251 (unsigned long long) dp->blocks30,
252 (unsigned long long) dp->blocks60,
253 (unsigned long long) dp->blocks90);
254 fputc('\n', fp);
255 }
256}
257
258static void
259quot_report_mount(
260 FILE *fp,
261 uint form,
262 uint type,
263 fs_path_t *mount,
264 uint flags)
265{
266 switch (type) {
267 case XFS_GROUP_QUOTA:
268 quot_report_mount_any_type(fp, du[1], ndu[1], gid_to_name,
269 form, type, mount, flags);
270 break;
271 case XFS_PROJ_QUOTA:
272 quot_report_mount_any_type(fp, du[2], ndu[2], prid_to_name,
273 form, type, mount, flags);
274 break;
275 case XFS_USER_QUOTA:
276 quot_report_mount_any_type(fp, du[0], ndu[0], uid_to_name,
277 form, type, mount, flags);
278 }
279}
280
281static void
282quot_report(
283 FILE *fp,
284 uint form,
285 uint type,
286 char *dir,
287 uint flags)
288{
289 fs_cursor_t cursor;
290 fs_path_t *mount;
291
292 now = time(0);
293 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
294 while ((mount = fs_cursor_next_entry(&cursor))) {
295 quot_bulkstat_mount(mount->fs_dir, flags);
296 quot_report_mount(fp, form, type, mount, flags);
297 }
298}
299
300static void
301quot_histogram_mount(
302 FILE *fp,
303 fs_path_t *mount,
304 uint flags)
305{
306 __uint64_t t = 0;
307 int i;
308
309 fprintf(fp, _("%s (%s):\n"), mount->fs_name, mount->fs_dir);
310
311 for (i = 0; i < TSIZE - 1; i++)
312 if (sizes[i] > 0) {
313 t += sizes[i] * i;
314 fprintf(fp, _("%d\t%llu\t%llu\n"), i,
315 (unsigned long long) sizes[i],
316 (unsigned long long) t);
317 }
318 fprintf(fp, _("%d\t%llu\t%llu\n"), TSIZE - 1,
319 (unsigned long long) sizes[TSIZE - 1],
320 (unsigned long long) (overflow + t));
321}
322
323static void
324quot_histogram(
325 FILE *fp,
326 char *dir,
327 uint flags)
328{
329 fs_cursor_t cursor;
330 fs_path_t *mount;
331
332 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
333 while ((mount = fs_cursor_next_entry(&cursor))) {
334 quot_bulkstat_mount(mount->fs_dir, flags);
335 quot_histogram_mount(fp, mount, flags);
336 }
337}
338
339static void
340quot_any_type(
341 FILE *fp,
342 uint form,
343 uint type,
344 char *dir,
345 uint flags)
346{
347 if (flags & HISTOGRAM_FLAG)
348 quot_histogram(fp, dir, flags);
349 else
350 quot_report(fp, form, type, dir, flags);
351}
352
353static int
354quot_f(
355 int argc,
356 char **argv)
357{
358 FILE *fp = NULL;
359 char *fname = NULL;
360 int c, flags = 0, type = 0, form = 0;
361
1774874a 362 while ((c = getopt(argc, argv, "abcf:ghinpruv")) != EOF) {
5aead01d
NS
363 switch (c) {
364 case 'f':
365 fname = optarg;
366 break;
367 case 'b':
368 form |= XFS_BLOCK_QUOTA;
369 break;
370 case 'i':
371 form |= XFS_INODE_QUOTA;
372 break;
373 case 'r':
374 form |= XFS_RTBLOCK_QUOTA;
375 break;
376 case 'g':
377 type = XFS_GROUP_QUOTA;
378 break;
379 case 'p':
380 type = XFS_PROJ_QUOTA;
381 break;
382 case 'u':
383 type = XFS_USER_QUOTA;
384 break;
385 case 'a':
386 flags |= ALL_MOUNTS_FLAG;
387 break;
388 case 'c':
389 flags |= HISTOGRAM_FLAG;
390 break;
1774874a
NS
391 case 'n':
392 flags |= NO_LOOKUP_FLAG;
393 break;
5aead01d
NS
394 case 'v':
395 flags |= VERBOSE_FLAG;
396 break;
397 default:
398 return command_usage(&quot_cmd);
399 }
400 }
401
402 if (!form)
403 form = XFS_BLOCK_QUOTA;
404
405 if (!type)
406 type = XFS_USER_QUOTA;
407
408 if ((fp = fopen_write_secure(fname)) == NULL)
409 return 0;
410
fa13a00f
NS
411 if (argc == optind) {
412 if (flags & ALL_MOUNTS_FLAG)
413 quot_any_type(fp, form, type, NULL, flags);
414 else if (fs_path->fs_flags & FS_MOUNT_POINT)
415 quot_any_type(fp, form, type, fs_path->fs_dir, flags);
416 } else while (argc > optind) {
5aead01d 417 quot_any_type(fp, form, type, argv[optind++], flags);
fa13a00f 418 }
5aead01d
NS
419
420 if (fname)
421 fclose(fp);
422 return 0;
423}
424
425void
426quot_init(void)
427{
428 quot_cmd.name = _("quot");
429 quot_cmd.cfunc = quot_f;
430 quot_cmd.argmin = 0;
431 quot_cmd.argmax = -1;
432 quot_cmd.args = _("[-bir] [-gpu] [-acv] [-f file]");
433 quot_cmd.oneline = _("summarize filesystem ownership");
434 quot_cmd.help = quot_help;
435
436 if (expert)
437 add_command(&quot_cmd);
438}