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